From 357134040febff5a7cbbce2aa3a7f41273233f28 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 1 Mar 2017 11:56:34 -0500 Subject: [PATCH] Update deps in support of drain --- .gitmodules | 18 + _vendor/github.com/MakeNowJust/heredoc | 1 + _vendor/github.com/chai2010/gettext-go | 1 + _vendor/github.com/daviddengcn/go-colortext | 1 + _vendor/github.com/docker/spdystream | 1 + _vendor/github.com/renstrom/dedent | 1 + _vendor/k8s.io/heapster | 1 + vendor/github.com/MakeNowJust/heredoc/LICENSE | 21 + .../github.com/MakeNowJust/heredoc/README.md | 53 + .../github.com/MakeNowJust/heredoc/dot/dot.go | 35 + .../MakeNowJust/heredoc/example_test.go | 60 + .../github.com/MakeNowJust/heredoc/heredoc.go | 89 + .../MakeNowJust/heredoc/heredoc_test.go | 68 + .../chai2010/gettext-go/.travis.yml | 7 + vendor/github.com/chai2010/gettext-go/LICENSE | 27 + .../github.com/chai2010/gettext-go/README.md | 58 + .../chai2010/gettext-go/examples/Makefile | 13 + .../chai2010/gettext-go/examples/hello.go | 83 + .../chai2010/gettext-go/examples/hi/hi.go | 17 + .../chai2010/gettext-go/examples/local.zip | Bin 0 -> 8633 bytes .../local/default/LC_MESSAGES/hello.mo | Bin 0 -> 476 bytes .../local/default/LC_MESSAGES/hello.po | 35 + .../default/LC_RESOURCE/hello/favicon.ico | Bin 0 -> 1150 bytes .../local/default/LC_RESOURCE/hello/poems.txt | 23 + .../examples/local/zh_CN/LC_MESSAGES/hello.mo | Bin 0 -> 813 bytes .../examples/local/zh_CN/LC_MESSAGES/hello.po | 35 + .../local/zh_CN/LC_RESOURCE/hello/poems.txt | 19 + .../examples/local/zh_TW/LC_MESSAGES/hello.mo | Bin 0 -> 813 bytes .../examples/local/zh_TW/LC_MESSAGES/hello.po | 35 + .../local/zh_TW/LC_RESOURCE/hello/poems.txt | 19 + .../chai2010/gettext-go/gettext/caller.go | 39 + .../gettext-go/gettext/caller_test.go | 89 + .../chai2010/gettext-go/gettext/doc.go | 66 + .../chai2010/gettext-go/gettext/domain.go | 119 + .../gettext-go/gettext/domain_helper.go | 50 + .../chai2010/gettext-go/gettext/fs.go | 187 + .../chai2010/gettext-go/gettext/gettext.go | 184 + .../gettext-go/gettext/gettext_test.go | 253 + .../chai2010/gettext-go/gettext/hello.go | 22 + .../chai2010/gettext-go/gettext/local.go | 34 + .../chai2010/gettext-go/gettext/mo/doc.go | 74 + .../chai2010/gettext-go/gettext/mo/encoder.go | 124 + .../gettext-go/gettext/mo/encoder_test.go | 55 + .../chai2010/gettext-go/gettext/mo/file.go | 193 + .../gettext-go/gettext/mo/file_test.go | 13 + .../chai2010/gettext-go/gettext/mo/header.go | 109 + .../gettext-go/gettext/mo/header_test.go | 13 + .../chai2010/gettext-go/gettext/mo/message.go | 39 + .../chai2010/gettext-go/gettext/mo/util.go | 110 + .../gettext-go/gettext/mo/util_test.go | 68 + .../chai2010/gettext-go/gettext/plural/doc.go | 36 + .../gettext-go/gettext/plural/formula.go | 181 + .../gettext-go/gettext/plural/formula_test.go | 50 + .../gettext-go/gettext/plural/table.go | 55 + .../chai2010/gettext-go/gettext/po/comment.go | 270 + .../gettext-go/gettext/po/comment_test.go | 207 + .../chai2010/gettext-go/gettext/po/doc.go | 24 + .../chai2010/gettext-go/gettext/po/file.go | 75 + .../gettext-go/gettext/po/file_test.go | 13 + .../chai2010/gettext-go/gettext/po/header.go | 106 + .../gettext-go/gettext/po/header_test.go | 13 + .../gettext-go/gettext/po/line_reader.go | 62 + .../chai2010/gettext-go/gettext/po/message.go | 189 + .../gettext-go/gettext/po/message_test.go | 75 + .../gettext-go/gettext/po/poedit_test.go | 35 + .../chai2010/gettext-go/gettext/po/re.go | 58 + .../chai2010/gettext-go/gettext/po/util.go | 110 + .../gettext-go/gettext/po/util_test.go | 68 + .../gettext-go/gettext/testdata_test.go | 62 + .../chai2010/gettext-go/gettext/tr.go | 128 + .../chai2010/gettext-go/gettext/tr_test.go | 100 + .../chai2010/gettext-go/testdata/Makefile | 14 + .../chai2010/gettext-go/testdata/README.txt | 1 + .../gettext-go/testdata/gettext-3-1.mo | Bin 0 -> 282 bytes .../gettext-go/testdata/gettext-3-1.po | 13 + .../gettext-go/testdata/gettext-3-2.mo | Bin 0 -> 282 bytes .../gettext-go/testdata/gettext-3-2.po | 13 + .../chai2010/gettext-go/testdata/gettext-4.mo | Bin 0 -> 190 bytes .../chai2010/gettext-go/testdata/gettext-4.po | 8 + .../chai2010/gettext-go/testdata/gettext-5.mo | Bin 0 -> 190 bytes .../chai2010/gettext-go/testdata/gettext-5.po | 8 + .../gettext-go/testdata/gettext-6-1.mo | Bin 0 -> 195 bytes .../gettext-go/testdata/gettext-6-1.po | 8 + .../gettext-go/testdata/gettext-6-2.mo | Bin 0 -> 192 bytes .../gettext-go/testdata/gettext-6-2.po | 8 + .../chai2010/gettext-go/testdata/gettext-7.mo | Bin 0 -> 190 bytes .../chai2010/gettext-go/testdata/gettext-7.po | 8 + .../gettext-go/testdata/gettextpo-1.de.mo | Bin 0 -> 681 bytes .../gettext-go/testdata/gettextpo-1.de.po | 43 + .../gettext-go/testdata/mm-ko-comp.euc-kr.mo | Bin 0 -> 5881 bytes .../gettext-go/testdata/mm-ko-comp.euc-kr.po | 1633 + .../gettext-go/testdata/mm-ko.euc-kr.mo | Bin 0 -> 11965 bytes .../gettext-go/testdata/mm-ko.euc-kr.po | 9096 +++ .../gettext-go/testdata/mm-viet.comp.mo | Bin 0 -> 1009843 bytes .../gettext-go/testdata/mm-viet.comp.po | 49553 ++++++++++++++++ .../gettext-go/testdata/poedit-1.5.7-zh_CN.mo | Bin 0 -> 27348 bytes .../gettext-go/testdata/poedit-1.5.7-zh_CN.po | 1591 + .../gettext-go/testdata/qttest2_de.mo | Bin 0 -> 512 bytes .../gettext-go/testdata/qttest2_de.po | 36 + .../chai2010/gettext-go/testdata/qttest_pl.mo | Bin 0 -> 624 bytes .../chai2010/gettext-go/testdata/qttest_pl.po | 26 + .../chai2010/gettext-go/testdata/test.mo | Bin 0 -> 493 bytes .../chai2010/gettext-go/testdata/test.po | 38 + .../chai2010/gettext-go/testdata/xg-c-1.ok.po | 727 + .../daviddengcn/go-colortext/.gitignore | 22 + .../daviddengcn/go-colortext/LICENSE | 54 + .../daviddengcn/go-colortext/README.md | 21 + .../github.com/daviddengcn/go-colortext/ct.go | 45 + .../daviddengcn/go-colortext/ct_ansi.go | 48 + .../daviddengcn/go-colortext/ct_ansi_test.go | 18 + .../daviddengcn/go-colortext/ct_test.go | 82 + .../daviddengcn/go-colortext/ct_win.go | 133 + .../docker/spdystream/CONTRIBUTING.md | 13 + vendor/github.com/docker/spdystream/LICENSE | 191 + .../github.com/docker/spdystream/LICENSE.docs | 425 + .../github.com/docker/spdystream/MAINTAINERS | 28 + vendor/github.com/docker/spdystream/README.md | 77 + .../docker/spdystream/connection.go | 958 + .../github.com/docker/spdystream/handlers.go | 38 + .../github.com/docker/spdystream/priority.go | 98 + .../docker/spdystream/priority_test.go | 108 + .../docker/spdystream/spdy/dictionary.go | 187 + .../github.com/docker/spdystream/spdy/read.go | 348 + .../docker/spdystream/spdy/spdy_test.go | 644 + .../docker/spdystream/spdy/types.go | 275 + .../docker/spdystream/spdy/write.go | 318 + .../docker/spdystream/spdy_bench_test.go | 113 + .../github.com/docker/spdystream/spdy_test.go | 1171 + vendor/github.com/docker/spdystream/stream.go | 327 + vendor/github.com/docker/spdystream/utils.go | 16 + .../docker/spdystream/ws/connection.go | 65 + .../docker/spdystream/ws/ws_test.go | 175 + vendor/github.com/renstrom/dedent/.travis.yml | 11 + vendor/github.com/renstrom/dedent/LICENSE | 21 + vendor/github.com/renstrom/dedent/README.md | 50 + vendor/github.com/renstrom/dedent/dedent.go | 49 + .../github.com/renstrom/dedent/dedent_test.go | 169 + vendor/k8s.io/heapster/.gitignore | 10 + vendor/k8s.io/heapster/.travis.yml | 16 + vendor/k8s.io/heapster/CONTRIBUTING.md | 14 + vendor/k8s.io/heapster/Godeps/Godeps.json | 953 + vendor/k8s.io/heapster/Godeps/Readme | 5 + vendor/k8s.io/heapster/LICENSE | 201 + vendor/k8s.io/heapster/Makefile | 49 + vendor/k8s.io/heapster/README.md | 40 + vendor/k8s.io/heapster/RELEASES.md | 125 + .../common/elasticsearch/elasticsearch.go | 142 + .../elasticsearch/elasticsearch_test.go | 69 + vendor/k8s.io/heapster/common/flags/flags.go | 77 + .../heapster/common/flags/flags_test.go | 176 + vendor/k8s.io/heapster/common/gce/gce.go | 38 + .../common/influxdb/dummy_influxdb.go | 64 + .../heapster/common/influxdb/influxdb.go | 109 + .../heapster/common/kubernetes/configs.go | 140 + .../heapster/deploy/docker-compose/README.md | 15 + .../deploy/docker-compose/docker-compose.yml | 28 + .../deploy/docker-compose/sample-hosts.json | 8 + .../k8s.io/heapster/deploy/docker/Dockerfile | 18 + vendor/k8s.io/heapster/deploy/docker/build.sh | 13 + .../heapster/deploy/docker/canary/Dockerfile | 9 + .../google/heapster-controller.yaml | 39 + .../kube-config/google/heapster-service.yaml | 14 + .../kube-config/influxdb/grafana-service.yaml | 17 + .../influxdb/heapster-controller.yaml | 28 + .../influxdb/heapster-service.yaml | 14 + .../influxdb/influxdb-grafana-controller.yaml | 47 + .../influxdb/influxdb-service.yaml | 16 + .../kafka/heapster-controller.yaml | 28 + .../kube-config/kafka/heapster-service.yaml | 14 + .../kube-config/kafka/kafka-controller.yaml | 22 + .../kube-config/kafka/kafka-service.yaml | 14 + .../riemann/riemann-controller.json | 49 + .../kube-config/riemann/riemann-service.json | 36 + .../standalone-test/heapster-controller.yaml | 44 + .../standalone-test/heapster-service.yaml | 11 + .../heapster-summary-controller.yaml | 44 + .../standalone/heapster-controller.yaml | 35 + .../standalone/heapster-service.yaml | 14 + vendor/k8s.io/heapster/deploy/kube.sh | 41 + vendor/k8s.io/heapster/docs/debugging.md | 72 + vendor/k8s.io/heapster/docs/google.md | 24 + vendor/k8s.io/heapster/docs/hacking.md | 11 + vendor/k8s.io/heapster/docs/influxdb.md | 30 + vendor/k8s.io/heapster/docs/integration.md | 7 + vendor/k8s.io/heapster/docs/model.md | 77 + vendor/k8s.io/heapster/docs/overview.md | 12 + .../heapster/docs/proposals/old-timer.md | 228 + .../heapster/docs/proposals/push-metrics.md | 233 + .../k8s.io/heapster/docs/proposals/vision.md | 130 + vendor/k8s.io/heapster/docs/riemann.md | 20 + .../heapster/docs/sink-configuration.md | 200 + .../heapster/docs/source-configuration.md | 87 + vendor/k8s.io/heapster/docs/storage-schema.md | 110 + vendor/k8s.io/heapster/events/core/types.go | 46 + vendor/k8s.io/heapster/events/eventer.go | 113 + .../k8s.io/heapster/events/manager/manager.go | 97 + .../heapster/events/manager/manager_test.go | 45 + .../events/sinks/elasticsearch/driver.go | 113 + .../events/sinks/elasticsearch/driver_test.go | 100 + .../k8s.io/heapster/events/sinks/factory.go | 63 + .../k8s.io/heapster/events/sinks/gcl/gcl.go | 97 + .../events/sinks/influxdb/influxdb.go | 223 + .../events/sinks/influxdb/influxdb_test.go | 104 + .../heapster/events/sinks/log/log_sink.go | 51 + .../events/sinks/log/log_sink_test.go | 49 + .../k8s.io/heapster/events/sinks/manager.go | 144 + .../heapster/events/sinks/manager_test.go | 114 + .../k8s.io/heapster/events/sources/factory.go | 58 + .../sources/kubernetes/kubernetes_source.go | 195 + vendor/k8s.io/heapster/events/util/dummies.go | 84 + vendor/k8s.io/heapster/grafana/Dockerfile | 14 + vendor/k8s.io/heapster/grafana/Makefile | 13 + vendor/k8s.io/heapster/grafana/README.md | 17 + vendor/k8s.io/heapster/grafana/RELEASES.md | 20 + .../heapster/grafana/dashboards/cluster.json | 2465 + .../dashboards/influxdb_withfields/README.md | 12 + .../influxdb_withfields/cluster.json | 2465 + .../influxdb_withfields/events.json | 389 + .../dashboards/influxdb_withfields/pods.json | 1060 + .../heapster/grafana/dashboards/pods.json | 1060 + vendor/k8s.io/heapster/grafana/run.sh | 69 + .../k8s.io/heapster/hooks/boilerplate.go.txt | 13 + .../heapster/hooks/check_boilerplate.sh | 33 + vendor/k8s.io/heapster/hooks/check_gofmt.sh | 21 + vendor/k8s.io/heapster/hooks/coverage.sh | 52 + vendor/k8s.io/heapster/hooks/run_vet.sh | 31 + vendor/k8s.io/heapster/influxdb/Dockerfile | 21 + vendor/k8s.io/heapster/influxdb/RELEASES.md | 16 + vendor/k8s.io/heapster/influxdb/build.sh | 5 + vendor/k8s.io/heapster/influxdb/config.toml | 100 + vendor/k8s.io/heapster/influxdb/start | 11 + .../k8s.io/heapster/integration/.jenkins.sh | 17 + .../k8s.io/heapster/integration/framework.go | 517 + .../heapster/integration/heapster_api_test.go | 1147 + .../k8s.io/heapster/integration/model_test.go | 158 + vendor/k8s.io/heapster/integration/utils.go | 74 + vendor/k8s.io/heapster/kafka/Dockerfile | 27 + vendor/k8s.io/heapster/kafka/README.md | 11 + vendor/k8s.io/heapster/kafka/build.sh | 5 + .../heapster/kafka/scripts/start-kafka.sh | 62 + .../heapster/kafka/supervisor/kafka.conf | 4 + .../heapster/kafka/supervisor/zookeeper.conf | 4 + vendor/k8s.io/heapster/metrics/api/v1/api.go | 281 + .../heapster/metrics/api/v1/api_test.go | 177 + .../metrics/api/v1/historical_handlers.go | 848 + .../api/v1/historical_handlers_test.go | 1520 + .../heapster/metrics/api/v1/model_handlers.go | 514 + .../metrics/api/v1/types/historical_types.go | 49 + .../metrics/api/v1/types/model_types.go | 63 + .../heapster/metrics/api/v1/types/types.go | 83 + .../heapster/metrics/apis/metrics/handlers.go | 293 + .../metrics/apis/metrics/v1alpha1/types.go | 78 + vendor/k8s.io/heapster/metrics/auth.go | 115 + .../heapster/metrics/core/historical_types.go | 171 + vendor/k8s.io/heapster/metrics/core/labels.go | 193 + .../k8s.io/heapster/metrics/core/metrics.go | 562 + .../k8s.io/heapster/metrics/core/ms_keys.go | 48 + vendor/k8s.io/heapster/metrics/core/types.go | 158 + vendor/k8s.io/heapster/metrics/handlers.go | 68 + vendor/k8s.io/heapster/metrics/heapster.go | 282 + .../heapster/metrics/manager/manager.go | 153 + .../heapster/metrics/manager/manager_test.go | 57 + .../metrics/processors/cluster_aggregator.go | 49 + .../processors/cluster_aggregator_test.go | 84 + .../heapster/metrics/processors/helper.go | 48 + .../processors/namespace_aggregator.go | 69 + .../processors/namespace_aggregator_test.go | 84 + .../processors/namespace_based_enricher.go | 91 + .../metrics/processors/node_aggregator.go | 54 + .../processors/node_aggregator_test.go | 94 + .../processors/node_autoscaling_enricher.go | 103 + .../metrics/processors/pod_aggregator.go | 126 + .../metrics/processors/pod_aggregator_test.go | 96 + .../metrics/processors/pod_based_enricher.go | 203 + .../processors/pod_based_enricher_test.go | 164 + .../metrics/processors/rate_calculator.go | 93 + .../processors/rate_calculator_test.go | 93 + .../metrics/sinks/elasticsearch/driver.go | 104 + .../sinks/elasticsearch/driver_test.go | 187 + .../k8s.io/heapster/metrics/sinks/factory.go | 109 + .../k8s.io/heapster/metrics/sinks/gcm/gcm.go | 341 + .../heapster/metrics/sinks/hawkular/client.go | 340 + .../heapster/metrics/sinks/hawkular/driver.go | 324 + .../metrics/sinks/hawkular/driver_test.go | 673 + .../heapster/metrics/sinks/hawkular/types.go | 73 + .../metrics/sinks/influxdb/influxdb.go | 233 + .../sinks/influxdb/influxdb_historical.go | 729 + .../metrics/sinks/influxdb/influxdb_test.go | 583 + .../heapster/metrics/sinks/kafka/driver.go | 170 + .../metrics/sinks/kafka/driver_test.go | 186 + .../heapster/metrics/sinks/log/log_sink.go | 119 + .../metrics/sinks/log/log_sink_test.go | 128 + .../k8s.io/heapster/metrics/sinks/manager.go | 159 + .../heapster/metrics/sinks/manager_test.go | 131 + .../metrics/sinks/metric/metric_sink.go | 346 + .../metrics/sinks/metric/metric_sink_test.go | 270 + .../heapster/metrics/sinks/monasca/config.go | 60 + .../metrics/sinks/monasca/data_test.go | 226 + .../heapster/metrics/sinks/monasca/driver.go | 195 + .../metrics/sinks/monasca/driver_test.go | 147 + .../metrics/sinks/monasca/init_test.go | 195 + .../metrics/sinks/monasca/keystone_client.go | 147 + .../sinks/monasca/keystone_client_test.go | 98 + .../metrics/sinks/monasca/monasca_client.go | 139 + .../sinks/monasca/monasca_client_test.go | 119 + .../heapster/metrics/sinks/opentsdb/driver.go | 209 + .../metrics/sinks/opentsdb/driver_test.go | 200 + .../heapster/metrics/sinks/riemann/driver.go | 213 + .../metrics/sinks/riemann/driver_test.go | 197 + .../heapster/metrics/sources/factory.go | 51 + .../metrics/sources/kubelet/cadvisor_utils.go | 23 + .../metrics/sources/kubelet/configs.go | 71 + .../metrics/sources/kubelet/kubelet.go | 347 + .../metrics/sources/kubelet/kubelet_client.go | 210 + .../sources/kubelet/kubelet_client_test.go | 104 + .../metrics/sources/kubelet/kubelet_test.go | 463 + .../heapster/metrics/sources/manager.go | 170 + .../heapster/metrics/sources/manager_test.go | 117 + .../metrics/sources/summary/summary.go | 462 + .../metrics/sources/summary/summary_test.go | 430 + .../k8s.io/heapster/metrics/util/dummies.go | 138 + .../heapster/metrics/util/metrics/http.go | 171 + vendor/k8s.io/heapster/metrics/util/util.go | 49 + vendor/k8s.io/heapster/riemann/Dockerfile | 12 + vendor/k8s.io/heapster/riemann/README.md | 4 + vendor/k8s.io/heapster/riemann/build.sh | 5 + .../riemann/riemann-pagerduty-sample.config | 93 + vendor/k8s.io/heapster/riemann/riemann.config | 26 + vendor/k8s.io/heapster/version/version.go | 22 + 329 files changed, 106779 insertions(+) create mode 160000 _vendor/github.com/MakeNowJust/heredoc create mode 160000 _vendor/github.com/chai2010/gettext-go create mode 160000 _vendor/github.com/daviddengcn/go-colortext create mode 160000 _vendor/github.com/docker/spdystream create mode 160000 _vendor/github.com/renstrom/dedent create mode 160000 _vendor/k8s.io/heapster create mode 100644 vendor/github.com/MakeNowJust/heredoc/LICENSE create mode 100644 vendor/github.com/MakeNowJust/heredoc/README.md create mode 100644 vendor/github.com/MakeNowJust/heredoc/dot/dot.go create mode 100644 vendor/github.com/MakeNowJust/heredoc/example_test.go create mode 100644 vendor/github.com/MakeNowJust/heredoc/heredoc.go create mode 100644 vendor/github.com/MakeNowJust/heredoc/heredoc_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/.travis.yml create mode 100644 vendor/github.com/chai2010/gettext-go/LICENSE create mode 100644 vendor/github.com/chai2010/gettext-go/README.md create mode 100644 vendor/github.com/chai2010/gettext-go/examples/Makefile create mode 100644 vendor/github.com/chai2010/gettext-go/examples/hello.go create mode 100644 vendor/github.com/chai2010/gettext-go/examples/hi/hi.go create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local.zip create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/default/LC_MESSAGES/hello.mo create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/default/LC_MESSAGES/hello.po create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/default/LC_RESOURCE/hello/favicon.ico create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/default/LC_RESOURCE/hello/poems.txt create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_MESSAGES/hello.mo create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_MESSAGES/hello.po create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_RESOURCE/hello/poems.txt create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_MESSAGES/hello.mo create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_MESSAGES/hello.po create mode 100644 vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_RESOURCE/hello/poems.txt create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/caller.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/caller_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/doc.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/domain.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/domain_helper.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/fs.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/gettext.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/gettext_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/hello.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/local.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/doc.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/encoder.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/encoder_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/file.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/file_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/header.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/header_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/message.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/util.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/mo/util_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/plural/doc.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/plural/formula.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/plural/formula_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/plural/table.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/comment.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/comment_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/doc.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/file.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/file_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/header.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/header_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/line_reader.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/message.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/message_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/poedit_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/re.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/util.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/po/util_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/testdata_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/tr.go create mode 100644 vendor/github.com/chai2010/gettext-go/gettext/tr_test.go create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/Makefile create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/README.txt create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-3-1.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-3-1.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-3-2.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-3-2.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-4.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-4.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-5.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-5.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-6-1.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-6-1.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-6-2.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-6-2.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-7.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettext-7.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/mm-ko.euc-kr.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/mm-ko.euc-kr.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/test.mo create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/test.po create mode 100644 vendor/github.com/chai2010/gettext-go/testdata/xg-c-1.ok.po create mode 100644 vendor/github.com/daviddengcn/go-colortext/.gitignore create mode 100644 vendor/github.com/daviddengcn/go-colortext/LICENSE create mode 100644 vendor/github.com/daviddengcn/go-colortext/README.md create mode 100644 vendor/github.com/daviddengcn/go-colortext/ct.go create mode 100644 vendor/github.com/daviddengcn/go-colortext/ct_ansi.go create mode 100644 vendor/github.com/daviddengcn/go-colortext/ct_ansi_test.go create mode 100644 vendor/github.com/daviddengcn/go-colortext/ct_test.go create mode 100644 vendor/github.com/daviddengcn/go-colortext/ct_win.go create mode 100644 vendor/github.com/docker/spdystream/CONTRIBUTING.md create mode 100644 vendor/github.com/docker/spdystream/LICENSE create mode 100644 vendor/github.com/docker/spdystream/LICENSE.docs create mode 100644 vendor/github.com/docker/spdystream/MAINTAINERS create mode 100644 vendor/github.com/docker/spdystream/README.md create mode 100644 vendor/github.com/docker/spdystream/connection.go create mode 100644 vendor/github.com/docker/spdystream/handlers.go create mode 100644 vendor/github.com/docker/spdystream/priority.go create mode 100644 vendor/github.com/docker/spdystream/priority_test.go create mode 100644 vendor/github.com/docker/spdystream/spdy/dictionary.go create mode 100644 vendor/github.com/docker/spdystream/spdy/read.go create mode 100644 vendor/github.com/docker/spdystream/spdy/spdy_test.go create mode 100644 vendor/github.com/docker/spdystream/spdy/types.go create mode 100644 vendor/github.com/docker/spdystream/spdy/write.go create mode 100644 vendor/github.com/docker/spdystream/spdy_bench_test.go create mode 100644 vendor/github.com/docker/spdystream/spdy_test.go create mode 100644 vendor/github.com/docker/spdystream/stream.go create mode 100644 vendor/github.com/docker/spdystream/utils.go create mode 100644 vendor/github.com/docker/spdystream/ws/connection.go create mode 100644 vendor/github.com/docker/spdystream/ws/ws_test.go create mode 100644 vendor/github.com/renstrom/dedent/.travis.yml create mode 100644 vendor/github.com/renstrom/dedent/LICENSE create mode 100644 vendor/github.com/renstrom/dedent/README.md create mode 100644 vendor/github.com/renstrom/dedent/dedent.go create mode 100644 vendor/github.com/renstrom/dedent/dedent_test.go create mode 100644 vendor/k8s.io/heapster/.gitignore create mode 100644 vendor/k8s.io/heapster/.travis.yml create mode 100644 vendor/k8s.io/heapster/CONTRIBUTING.md create mode 100644 vendor/k8s.io/heapster/Godeps/Godeps.json create mode 100644 vendor/k8s.io/heapster/Godeps/Readme create mode 100644 vendor/k8s.io/heapster/LICENSE create mode 100644 vendor/k8s.io/heapster/Makefile create mode 100755 vendor/k8s.io/heapster/README.md create mode 100644 vendor/k8s.io/heapster/RELEASES.md create mode 100644 vendor/k8s.io/heapster/common/elasticsearch/elasticsearch.go create mode 100644 vendor/k8s.io/heapster/common/elasticsearch/elasticsearch_test.go create mode 100644 vendor/k8s.io/heapster/common/flags/flags.go create mode 100644 vendor/k8s.io/heapster/common/flags/flags_test.go create mode 100644 vendor/k8s.io/heapster/common/gce/gce.go create mode 100644 vendor/k8s.io/heapster/common/influxdb/dummy_influxdb.go create mode 100644 vendor/k8s.io/heapster/common/influxdb/influxdb.go create mode 100644 vendor/k8s.io/heapster/common/kubernetes/configs.go create mode 100644 vendor/k8s.io/heapster/deploy/docker-compose/README.md create mode 100644 vendor/k8s.io/heapster/deploy/docker-compose/docker-compose.yml create mode 100644 vendor/k8s.io/heapster/deploy/docker-compose/sample-hosts.json create mode 100644 vendor/k8s.io/heapster/deploy/docker/Dockerfile create mode 100755 vendor/k8s.io/heapster/deploy/docker/build.sh create mode 100644 vendor/k8s.io/heapster/deploy/docker/canary/Dockerfile create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/google/heapster-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/google/heapster-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/influxdb/grafana-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-grafana-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-controller.json create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-service.json create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-service.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-summary-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-controller.yaml create mode 100644 vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-service.yaml create mode 100755 vendor/k8s.io/heapster/deploy/kube.sh create mode 100644 vendor/k8s.io/heapster/docs/debugging.md create mode 100644 vendor/k8s.io/heapster/docs/google.md create mode 100644 vendor/k8s.io/heapster/docs/hacking.md create mode 100644 vendor/k8s.io/heapster/docs/influxdb.md create mode 100644 vendor/k8s.io/heapster/docs/integration.md create mode 100644 vendor/k8s.io/heapster/docs/model.md create mode 100644 vendor/k8s.io/heapster/docs/overview.md create mode 100644 vendor/k8s.io/heapster/docs/proposals/old-timer.md create mode 100644 vendor/k8s.io/heapster/docs/proposals/push-metrics.md create mode 100644 vendor/k8s.io/heapster/docs/proposals/vision.md create mode 100644 vendor/k8s.io/heapster/docs/riemann.md create mode 100644 vendor/k8s.io/heapster/docs/sink-configuration.md create mode 100644 vendor/k8s.io/heapster/docs/source-configuration.md create mode 100644 vendor/k8s.io/heapster/docs/storage-schema.md create mode 100644 vendor/k8s.io/heapster/events/core/types.go create mode 100644 vendor/k8s.io/heapster/events/eventer.go create mode 100644 vendor/k8s.io/heapster/events/manager/manager.go create mode 100644 vendor/k8s.io/heapster/events/manager/manager_test.go create mode 100644 vendor/k8s.io/heapster/events/sinks/elasticsearch/driver.go create mode 100644 vendor/k8s.io/heapster/events/sinks/elasticsearch/driver_test.go create mode 100644 vendor/k8s.io/heapster/events/sinks/factory.go create mode 100644 vendor/k8s.io/heapster/events/sinks/gcl/gcl.go create mode 100644 vendor/k8s.io/heapster/events/sinks/influxdb/influxdb.go create mode 100644 vendor/k8s.io/heapster/events/sinks/influxdb/influxdb_test.go create mode 100644 vendor/k8s.io/heapster/events/sinks/log/log_sink.go create mode 100644 vendor/k8s.io/heapster/events/sinks/log/log_sink_test.go create mode 100644 vendor/k8s.io/heapster/events/sinks/manager.go create mode 100644 vendor/k8s.io/heapster/events/sinks/manager_test.go create mode 100644 vendor/k8s.io/heapster/events/sources/factory.go create mode 100644 vendor/k8s.io/heapster/events/sources/kubernetes/kubernetes_source.go create mode 100644 vendor/k8s.io/heapster/events/util/dummies.go create mode 100644 vendor/k8s.io/heapster/grafana/Dockerfile create mode 100644 vendor/k8s.io/heapster/grafana/Makefile create mode 100644 vendor/k8s.io/heapster/grafana/README.md create mode 100644 vendor/k8s.io/heapster/grafana/RELEASES.md create mode 100644 vendor/k8s.io/heapster/grafana/dashboards/cluster.json create mode 100644 vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/README.md create mode 100644 vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/cluster.json create mode 100644 vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/events.json create mode 100644 vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/pods.json create mode 100644 vendor/k8s.io/heapster/grafana/dashboards/pods.json create mode 100755 vendor/k8s.io/heapster/grafana/run.sh create mode 100644 vendor/k8s.io/heapster/hooks/boilerplate.go.txt create mode 100755 vendor/k8s.io/heapster/hooks/check_boilerplate.sh create mode 100755 vendor/k8s.io/heapster/hooks/check_gofmt.sh create mode 100755 vendor/k8s.io/heapster/hooks/coverage.sh create mode 100755 vendor/k8s.io/heapster/hooks/run_vet.sh create mode 100644 vendor/k8s.io/heapster/influxdb/Dockerfile create mode 100644 vendor/k8s.io/heapster/influxdb/RELEASES.md create mode 100755 vendor/k8s.io/heapster/influxdb/build.sh create mode 100644 vendor/k8s.io/heapster/influxdb/config.toml create mode 100755 vendor/k8s.io/heapster/influxdb/start create mode 100755 vendor/k8s.io/heapster/integration/.jenkins.sh create mode 100644 vendor/k8s.io/heapster/integration/framework.go create mode 100644 vendor/k8s.io/heapster/integration/heapster_api_test.go create mode 100644 vendor/k8s.io/heapster/integration/model_test.go create mode 100644 vendor/k8s.io/heapster/integration/utils.go create mode 100644 vendor/k8s.io/heapster/kafka/Dockerfile create mode 100644 vendor/k8s.io/heapster/kafka/README.md create mode 100644 vendor/k8s.io/heapster/kafka/build.sh create mode 100644 vendor/k8s.io/heapster/kafka/scripts/start-kafka.sh create mode 100644 vendor/k8s.io/heapster/kafka/supervisor/kafka.conf create mode 100644 vendor/k8s.io/heapster/kafka/supervisor/zookeeper.conf create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/api.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/api_test.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/historical_handlers.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/historical_handlers_test.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/model_handlers.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/types/historical_types.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/types/model_types.go create mode 100644 vendor/k8s.io/heapster/metrics/api/v1/types/types.go create mode 100644 vendor/k8s.io/heapster/metrics/apis/metrics/handlers.go create mode 100644 vendor/k8s.io/heapster/metrics/apis/metrics/v1alpha1/types.go create mode 100644 vendor/k8s.io/heapster/metrics/auth.go create mode 100644 vendor/k8s.io/heapster/metrics/core/historical_types.go create mode 100644 vendor/k8s.io/heapster/metrics/core/labels.go create mode 100644 vendor/k8s.io/heapster/metrics/core/metrics.go create mode 100644 vendor/k8s.io/heapster/metrics/core/ms_keys.go create mode 100644 vendor/k8s.io/heapster/metrics/core/types.go create mode 100644 vendor/k8s.io/heapster/metrics/handlers.go create mode 100644 vendor/k8s.io/heapster/metrics/heapster.go create mode 100644 vendor/k8s.io/heapster/metrics/manager/manager.go create mode 100644 vendor/k8s.io/heapster/metrics/manager/manager_test.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/cluster_aggregator.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/cluster_aggregator_test.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/helper.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/namespace_aggregator.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/namespace_aggregator_test.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/namespace_based_enricher.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/node_aggregator.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/node_aggregator_test.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/pod_aggregator.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/pod_aggregator_test.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/pod_based_enricher.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/pod_based_enricher_test.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/rate_calculator.go create mode 100644 vendor/k8s.io/heapster/metrics/processors/rate_calculator_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/factory.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/gcm/gcm.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/hawkular/client.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/hawkular/driver.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/hawkular/driver_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/hawkular/types.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_historical.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/kafka/driver.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/kafka/driver_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/log/log_sink.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/log/log_sink_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/manager.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/manager_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/config.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/data_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/driver.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/driver_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/init_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/riemann/driver.go create mode 100644 vendor/k8s.io/heapster/metrics/sinks/riemann/driver_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/factory.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/kubelet/cadvisor_utils.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/kubelet/configs.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/manager.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/manager_test.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/summary/summary.go create mode 100644 vendor/k8s.io/heapster/metrics/sources/summary/summary_test.go create mode 100644 vendor/k8s.io/heapster/metrics/util/dummies.go create mode 100644 vendor/k8s.io/heapster/metrics/util/metrics/http.go create mode 100644 vendor/k8s.io/heapster/metrics/util/util.go create mode 100644 vendor/k8s.io/heapster/riemann/Dockerfile create mode 100644 vendor/k8s.io/heapster/riemann/README.md create mode 100755 vendor/k8s.io/heapster/riemann/build.sh create mode 100644 vendor/k8s.io/heapster/riemann/riemann-pagerduty-sample.config create mode 100644 vendor/k8s.io/heapster/riemann/riemann.config create mode 100644 vendor/k8s.io/heapster/version/version.go diff --git a/.gitmodules b/.gitmodules index 028f864671..dbd96ca2f7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -232,3 +232,21 @@ [submodule "_vendor/k8s.io/gengo"] path = _vendor/k8s.io/gengo url = https://github.com/kubernetes/gengo.git +[submodule "_vendor/k8s.io/heapster"] + path = _vendor/k8s.io/heapster + url = https://github.com/kubernetes/heapster +[submodule "_vendor/github.com/MakeNowJust/heredoc"] + path = _vendor/github.com/MakeNowJust/heredoc + url = https://github.com/MakeNowJust/heredoc +[submodule "_vendor/github.com/daviddengcn/go-colortext"] + path = _vendor/github.com/daviddengcn/go-colortext + url = https://github.com/daviddengcn/go-colortext +[submodule "_vendor/github.com/docker/spdystream"] + path = _vendor/github.com/docker/spdystream + url = https://github.com/docker/spdystream +[submodule "_vendor/github.com/renstrom/dedent"] + path = _vendor/github.com/renstrom/dedent + url = https://github.com/renstrom/dedent +[submodule "_vendor/github.com/chai2010/gettext-go"] + path = _vendor/github.com/chai2010/gettext-go + url = https://github.com/chai2010/gettext-go diff --git a/_vendor/github.com/MakeNowJust/heredoc b/_vendor/github.com/MakeNowJust/heredoc new file mode 160000 index 0000000000..1d91351acd --- /dev/null +++ b/_vendor/github.com/MakeNowJust/heredoc @@ -0,0 +1 @@ +Subproject commit 1d91351acdc1cb2f2c995864674b754134b86ca7 diff --git a/_vendor/github.com/chai2010/gettext-go b/_vendor/github.com/chai2010/gettext-go new file mode 160000 index 0000000000..c6fed771bf --- /dev/null +++ b/_vendor/github.com/chai2010/gettext-go @@ -0,0 +1 @@ +Subproject commit c6fed771bfd517099caf0f7a961671fa8ed08723 diff --git a/_vendor/github.com/daviddengcn/go-colortext b/_vendor/github.com/daviddengcn/go-colortext new file mode 160000 index 0000000000..511bcaf42c --- /dev/null +++ b/_vendor/github.com/daviddengcn/go-colortext @@ -0,0 +1 @@ +Subproject commit 511bcaf42ccd42c38aba7427b6673277bf19e2a1 diff --git a/_vendor/github.com/docker/spdystream b/_vendor/github.com/docker/spdystream new file mode 160000 index 0000000000..449fdfce4d --- /dev/null +++ b/_vendor/github.com/docker/spdystream @@ -0,0 +1 @@ +Subproject commit 449fdfce4d962303d702fec724ef0ad181c92528 diff --git a/_vendor/github.com/renstrom/dedent b/_vendor/github.com/renstrom/dedent new file mode 160000 index 0000000000..020d11c3b9 --- /dev/null +++ b/_vendor/github.com/renstrom/dedent @@ -0,0 +1 @@ +Subproject commit 020d11c3b9c0c7a3c2efcc8e5cf5b9ef7bcea21f diff --git a/_vendor/k8s.io/heapster b/_vendor/k8s.io/heapster new file mode 160000 index 0000000000..c2ac40f1ad --- /dev/null +++ b/_vendor/k8s.io/heapster @@ -0,0 +1 @@ +Subproject commit c2ac40f1adf8c42a79badddb2a2acd673cae3bcb diff --git a/vendor/github.com/MakeNowJust/heredoc/LICENSE b/vendor/github.com/MakeNowJust/heredoc/LICENSE new file mode 100644 index 0000000000..dd5aa7e8a4 --- /dev/null +++ b/vendor/github.com/MakeNowJust/heredoc/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 TSUYUSATO Kitsune + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/MakeNowJust/heredoc/README.md b/vendor/github.com/MakeNowJust/heredoc/README.md new file mode 100644 index 0000000000..df12b43db6 --- /dev/null +++ b/vendor/github.com/MakeNowJust/heredoc/README.md @@ -0,0 +1,53 @@ +#heredoc [![Build Status](https://drone.io/github.com/MakeNowJust/heredoc/status.png)](https://drone.io/github.com/MakeNowJust/heredoc/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](https://gowalker.org/github.com/MakeNowJust/heredoc) + +##About + +Package heredoc provides the here-document with keeping indent. + +##Install + +```console +$ go get github.com/MakeNowJust/heredoc +``` + +##Import + +```go +// usual +import "github.com/MakeNowJust/heredoc" +// shortcuts +import . "github.com/MakeNowJust/heredoc/dot" +``` + +##Example + +```go +package main + +import ( + "fmt" + . "github.com/MakeNowJust/heredoc/dot" +) + +func main() { + fmt.Println(D(` + Lorem ipsum dolor sit amet, consectetur adipisicing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, ... + `)) + // Output: + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, + // sed do eiusmod tempor incididunt ut labore et dolore magna + // aliqua. Ut enim ad minim veniam, ... + // +} +``` + +##API Document + + - [Go Walker - github.com/MakeNowJust/heredoc](https://gowalker.org/github.com/MakeNowJust/heredoc) + - [Go Walker - github.com/MakeNowJust/heredoc/dot](https://gowalker.org/github.com/MakeNowJust/heredoc/dot) + +##License + +This software is released under the MIT License, see LICENSE. diff --git a/vendor/github.com/MakeNowJust/heredoc/dot/dot.go b/vendor/github.com/MakeNowJust/heredoc/dot/dot.go new file mode 100644 index 0000000000..e0e2af2600 --- /dev/null +++ b/vendor/github.com/MakeNowJust/heredoc/dot/dot.go @@ -0,0 +1,35 @@ +// Copyright (c) 2014 TSUYUSATO Kitsune +// This software is released under the MIT License. +// http://opensource.org/licenses/mit-license.php + +// Package heredoc_dot is the set of shortcuts for dot import. +// +// For example: +// package main +// +// import ( +// "fmt" +// "runtime" +// . "github.com/MakeNowJust/heredoc/dot" +// ) +// +// func main() { +// fmt.Printf(D(` +// GOROOT: %s +// GOARCH: %s +// GOOS : %s +// `), runtime.GOROOT(), runtime.GOARCH, runtime.GOOS) +// } +package heredoc_dot + +import "github.com/MakeNowJust/heredoc" + +// Shortcut heredoc.Doc +func D(raw string) string { + return heredoc.Doc(raw) +} + +// Shortcut heredoc.Docf +func Df(raw string, args ...interface{}) string { + return heredoc.Docf(raw, args...) +} diff --git a/vendor/github.com/MakeNowJust/heredoc/example_test.go b/vendor/github.com/MakeNowJust/heredoc/example_test.go new file mode 100644 index 0000000000..ed9840e6bd --- /dev/null +++ b/vendor/github.com/MakeNowJust/heredoc/example_test.go @@ -0,0 +1,60 @@ +// Copyright (c) 2014 TSUYUSATO Kitsune +// This software is released under the MIT License. +// http://opensource.org/licenses/mit-license.php + +package heredoc_test + +import ( + "fmt" +) + +import "github.com/MakeNowJust/heredoc" + +func ExampleDoc_lipsum() { + fmt.Print(heredoc.Doc(` + Lorem ipsum dolor sit amet, consectetur adipisicing elit, + sed do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, ... + `)) + // Output: + // Lorem ipsum dolor sit amet, consectetur adipisicing elit, + // sed do eiusmod tempor incididunt ut labore et dolore magna + // aliqua. Ut enim ad minim veniam, ... + // +} + +func ExampleDoc_spec() { + // Single line string is no change. + fmt.Println(heredoc.Doc(`It is single line.`)) + // If first line is empty, heredoc.Doc removes first line. + fmt.Println(heredoc.Doc(` + It is first line. + It is second line.`)) + // If last line is empty and more little length than indents, + // heredoc.Doc removes last line's content. + fmt.Println(heredoc.Doc(` + Next is last line. + `)) + fmt.Println("Previous is last line.") + // Output: + // It is single line. + // It is first line. + // It is second line. + // Next is last line. + // + // Previous is last line. +} + +func ExampleDocf() { + libName := "github.com/MakeNowJust/heredoc" + author := "TSUYUSATO Kitsune (@MakeNowJust)" + fmt.Printf(heredoc.Docf(` + Library Name : %s + Author : %s + Repository URL: http://%s.git + `, libName, author, libName)) + // Output: + // Library Name : github.com/MakeNowJust/heredoc + // Author : TSUYUSATO Kitsune (@MakeNowJust) + // Repository URL: http://github.com/MakeNowJust/heredoc.git +} diff --git a/vendor/github.com/MakeNowJust/heredoc/heredoc.go b/vendor/github.com/MakeNowJust/heredoc/heredoc.go new file mode 100644 index 0000000000..3978e30daf --- /dev/null +++ b/vendor/github.com/MakeNowJust/heredoc/heredoc.go @@ -0,0 +1,89 @@ +// Copyright (c) 2014 TSUYUSATO Kitsune +// This software is released under the MIT License. +// http://opensource.org/licenses/mit-license.php + +// Package heredoc provides the here-document with keeping indent. +// +// Golang supports raw-string syntax. +// doc := ` +// Foo +// Bar +// ` +// But raw-string cannot recognize indent. Thus such content is indented string, equivalent to +// "\n\tFoo\n\tBar\n" +// I dont't want this! +// +// However this problem is solved by package heredoc. +// doc := heredoc.Doc(` +// Foo +// Bar +// `) +// It is equivalent to +// "Foo\nBar\n" +package heredoc + +import ( + "fmt" + "strings" + "unicode" +) + +// heredoc.Doc retutns unindented string as here-document. +// +// Process of making here-document: +// 1. Find most little indent size. (Skip empty lines) +// 2. Remove this indents of lines. +func Doc(raw string) string { + skipFirstLine := false + if raw[0] == '\n' { + raw = raw[1:] + } else { + skipFirstLine = true + } + + minIndentSize := int(^uint(0) >> 1) // Max value of type int + lines := strings.Split(raw, "\n") + + // 1. + for i, line := range lines { + if i == 0 && skipFirstLine { + continue + } + + indentSize := 0 + for _, r := range []rune(line) { + if unicode.IsSpace(r) { + indentSize += 1 + } else { + break + } + } + + if len(line) == indentSize { + if i == len(lines)-1 && indentSize < minIndentSize { + lines[i] = "" + } + } else if indentSize < minIndentSize { + minIndentSize = indentSize + } + } + + // 2. + for i, line := range lines { + if i == 0 && skipFirstLine { + continue + } + + if len(lines[i]) >= minIndentSize { + lines[i] = line[minIndentSize:] + } + } + + return strings.Join(lines, "\n") +} + +// heredoc.Docf returns unindented and formatted string as here-document. +// This format is same with package fmt's format. +func Docf(raw string, args ...interface{}) string { + return fmt.Sprintf(Doc(raw), args...) +} diff --git a/vendor/github.com/MakeNowJust/heredoc/heredoc_test.go b/vendor/github.com/MakeNowJust/heredoc/heredoc_test.go new file mode 100644 index 0000000000..d8d7fa11ca --- /dev/null +++ b/vendor/github.com/MakeNowJust/heredoc/heredoc_test.go @@ -0,0 +1,68 @@ +// Copyright (c) 2014 TSUYUSATO Kitsune +// This software is released under the MIT License. +// http://opensource.org/licenses/mit-license.php + +package heredoc_test + +import ( + "testing" +) + +import "github.com/MakeNowJust/heredoc" + +type testCase struct { + raw, expect string +} + +var tests = []testCase{ + {` + Foo + Bar + `, + "Foo\nBar\n"}, + {`Foo + Bar`, + "Foo\nBar"}, + {`Foo + + Bar + `, + "Foo\n\t\nBar\n"}, + {` + Foo + Bar + Hoge + `, + "Foo\n\tBar\n\t\tHoge\n\t\t\t"}, + {`Foo Bar`, "Foo Bar"}, + { + ` + Foo + Bar + `, "Foo\nBar\n"}, +} + +func TestDoc(t *testing.T) { + for i, test := range tests { + result := heredoc.Doc(test.raw) + if result != test.expect { + t.Errorf("tests[%d] failed: expected=> %#v, result=> %#v", i, test.expect, result) + } + } +} + +func TestDocf(t *testing.T) { + // test case + str := ` + int: %3d + string: %s + ` + i := 42 + s := "Hello" + expect := "int: 42\nstring: Hello\n" + + result := heredoc.Docf(str, i, s) + if result != expect { + t.Errorf("test failed: expected=> %#v, result=> %#v", expect, result) + } +} diff --git a/vendor/github.com/chai2010/gettext-go/.travis.yml b/vendor/github.com/chai2010/gettext-go/.travis.yml new file mode 100644 index 0000000000..fb74de6b87 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.3 + - 1.4 + - 1.5 + - tip diff --git a/vendor/github.com/chai2010/gettext-go/LICENSE b/vendor/github.com/chai2010/gettext-go/LICENSE new file mode 100644 index 0000000000..8f39408250 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/LICENSE @@ -0,0 +1,27 @@ +Copyright 2013 ChaiShushan . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/chai2010/gettext-go/README.md b/vendor/github.com/chai2010/gettext-go/README.md new file mode 100644 index 0000000000..75a5a16f46 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/README.md @@ -0,0 +1,58 @@ +gettext-go +========== + +PkgDoc: [http://godoc.org/github.com/chai2010/gettext-go/gettext](http://godoc.org/github.com/chai2010/gettext-go/gettext) + +Install +======== + +1. `go get github.com/chai2010/gettext-go/gettext` +2. `go run hello.go` + +The godoc.org or gowalker.org has more information. + +Example +======= + +```Go +package main + +import ( + "fmt" + + "github.com/chai2010/gettext-go/gettext" +) + +func main() { + gettext.SetLocale("zh_CN") + gettext.Textdomain("hello") + + gettext.BindTextdomain("hello", "local", nil) + + // gettext.BindTextdomain("hello", "local", nil) // from local dir + // gettext.BindTextdomain("hello", "local.zip", nil) // from local zip file + // gettext.BindTextdomain("hello", "local.zip", zipData) // from embedded zip data + + // translate source text + fmt.Println(gettext.Gettext("Hello, world!")) + // Output: 你好, 世界! + + // if no msgctxt in PO file (only msgid and msgstr), + // specify context as "" by + fmt.Println(gettext.PGettext("", "Hello, world!")) + // Output: 你好, 世界! + + // translate resource + fmt.Println(string(gettext.Getdata("poems.txt")))) + // Output: ... +} +``` + +Go file: [hello.go](https://github.com/chai2010/gettext-go/blob/master/examples/hello.go); PO file: [hello.po](https://github.com/chai2010/gettext-go/blob/master/examples/local/default/LC_MESSAGES/hello.po); + +BUGS +==== + +Please report bugs to . + +Thanks! diff --git a/vendor/github.com/chai2010/gettext-go/examples/Makefile b/vendor/github.com/chai2010/gettext-go/examples/Makefile new file mode 100644 index 0000000000..b2eaea9c97 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/Makefile @@ -0,0 +1,13 @@ +# Copyright 2013 ChaiShushan . All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +default: + msgfmt -o local/default/LC_MESSAGES/hello.mo local/default/LC_MESSAGES/hello.po + msgfmt -o local/zh_CN/LC_MESSAGES/hello.mo local/zh_CN/LC_MESSAGES/hello.po + msgfmt -o local/zh_TW/LC_MESSAGES/hello.mo local/zh_TW/LC_MESSAGES/hello.po + 7z a -tzip -scsUTF-8 local.zip local + go run hello.go + +clean: + rm local.zip diff --git a/vendor/github.com/chai2010/gettext-go/examples/hello.go b/vendor/github.com/chai2010/gettext-go/examples/hello.go new file mode 100644 index 0000000000..7622c4b420 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/hello.go @@ -0,0 +1,83 @@ +// Copyright 2013 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a gettext-go exmaple. +package main + +import ( + "fmt" + + "github.com/chai2010/gettext-go/examples/hi" + "github.com/chai2010/gettext-go/gettext" +) + +func init() { + // bind app domain + gettext.BindTextdomain("hello", "local", nil) + gettext.Textdomain("hello") + + // $(LC_MESSAGES) or $(LANG) or empty + fmt.Println(gettext.Gettext("Gettext in init.")) + fmt.Println(gettext.PGettext("main.init", "Gettext in init.")) + hi.SayHi() + // Output(depends on local environment): + // ? + // ? + // ? + // ? + + // set simple chinese + gettext.SetLocale("zh_CN") + + // simple chinese + fmt.Println(gettext.Gettext("Gettext in init.")) + fmt.Println(gettext.PGettext("main.init", "Gettext in init.")) + hi.SayHi() + // Output: + // Init函数中的Gettext. + // Init函数中的Gettext. + // 来自"Hi"包的问候: 你好, 世界! + // 来自"Hi"包的问候: 你好, 世界! +} + +func main() { + // simple chinese + fmt.Println(gettext.Gettext("Hello, world!")) + fmt.Println(gettext.PGettext("main.main", "Hello, world!")) + hi.SayHi() + // Output: + // 你好, 世界! + // 你好, 世界! + // 来自"Hi"包的问候: 你好, 世界! + // 来自"Hi"包的问候: 你好, 世界! + + // set traditional chinese + gettext.SetLocale("zh_TW") + + // traditional chinese + func() { + fmt.Println(gettext.Gettext("Gettext in func.")) + fmt.Println(gettext.PGettext("main.func", "Gettext in func.")) + hi.SayHi() + // Output: + // 閉包函數中的Gettext. + // 閉包函數中的Gettext. + // 來自"Hi"包的問候: 你好, 世界! + // 來自"Hi"包的問候: 你好, 世界! + }() + + fmt.Println() + + // translate resource + gettext.SetLocale("zh_CN") + fmt.Println("poems(simple chinese):") + fmt.Println(string(gettext.Getdata("poems.txt"))) + gettext.SetLocale("zh_TW") + fmt.Println("poems(traditional chinese):") + fmt.Println(string(gettext.Getdata("poems.txt"))) + gettext.SetLocale("??") + fmt.Println("poems(default is english):") + fmt.Println(string(gettext.Getdata("poems.txt"))) + // Output: ... +} diff --git a/vendor/github.com/chai2010/gettext-go/examples/hi/hi.go b/vendor/github.com/chai2010/gettext-go/examples/hi/hi.go new file mode 100644 index 0000000000..93a52f5a3f --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/hi/hi.go @@ -0,0 +1,17 @@ +// Copyright 2013 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package hi is a example pkg. +package hi + +import ( + "fmt" + + "github.com/chai2010/gettext-go/gettext" +) + +func SayHi() { + fmt.Println(gettext.Gettext("pkg hi: Hello, world!")) + fmt.Println(gettext.PGettext("code.google.com/p/gettext-go/examples/hi.SayHi", "pkg hi: Hello, world!")) +} diff --git a/vendor/github.com/chai2010/gettext-go/examples/local.zip b/vendor/github.com/chai2010/gettext-go/examples/local.zip new file mode 100644 index 0000000000000000000000000000000000000000..877f10df6f3f5e89cbabb4371cbd07beec1cc76b GIT binary patch literal 8633 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 Qu2iiPjM}mxYwLD?0OW#<&j0`b literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/examples/local/default/LC_MESSAGES/hello.po b/vendor/github.com/chai2010/gettext-go/examples/local/default/LC_MESSAGES/hello.po new file mode 100644 index 0000000000..ac094c6d9e --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/local/default/LC_MESSAGES/hello.po @@ -0,0 +1,35 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: gettext-go-examples-hello\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-12-12 20:03+0000\n" +"PO-Revision-Date: 2013-12-30 20:47+0800\n" +"Last-Translator: chai2010 \n" +"Language-Team: chai2010(团队) \n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-SourceCharset: UTF-8\n" + +msgctxt "main.init" +msgid "Gettext in init." +msgstr "" + +msgctxt "main.main" +msgid "Hello, world!" +msgstr "" + +msgctxt "main.func" +msgid "Gettext in func." +msgstr "" + +msgctxt "github.com/chai2010/gettext-go/examples/hi.SayHi" +msgid "pkg hi: Hello, world!" +msgstr "" diff --git a/vendor/github.com/chai2010/gettext-go/examples/local/default/LC_RESOURCE/hello/favicon.ico b/vendor/github.com/chai2010/gettext-go/examples/local/default/LC_RESOURCE/hello/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d287722d806eed6369720b3cb073ce678e9390bc GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x$A1^@s5X9)4L`2X)=8A%wV2c&mY zKkdTr{O-#Chc;D#`g_y6y|{)5@Dut0V{hz(W?)PwAX>SCxFKyi?HFugGP%)x2@ zPwnsjzjRW_e~?<3UZA_cdFbJzr~ens=>f}ur;n2X>=5tU9ZDoM}IW|b`C199`%1y`436mQa*?aZ{!b}v0Wnt1Y{ zL?PETJ`g34ixBac%O3m({sK=siHm>2>dc0XL9m#Q>Y~1?{;H;bp0qj$>?q(YU}cT z0OoEKavh}$P9R%_AfkN2ExLt}Y0Z{231xvT(N;TVNg?9SJYBAGtNl2{5%+MF#jza6 zPo;{RBdA5Wa28V$ShIOC<^r48$x~c#y+38*LCTa`e|Kvr`G^IY)SBczQwf*C!$VPI zPZWua278XRrP8FHguF>^rC~yR>0!HZ*C!RlsD^SfMK$vlk>1H6)Jc$X)~+%oWP(?k zjJAgSi`j?u{@YKN{#)naXVJsT9lKtuPY*XybX>b4g=PZeEVp4U^S27^7#4Zeh*AmD z*YEi=q&(yq*^5k(X%WaK7omsC3tZa|$Se~~bmWvT*J$=iB3 z?~ArSZv0yLGFs)M+3L%k_b>WuU$f`$JlyMk&NjN^xVQCYr?WbO`fHm&$X2@Boo~IZ SO<)clh=XeI2L72i*y9i5w*vwI literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_MESSAGES/hello.po b/vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_MESSAGES/hello.po new file mode 100644 index 0000000000..3670d4f57c --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_MESSAGES/hello.po @@ -0,0 +1,35 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: gettext-go-examples-hello\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-12-12 20:03+0000\n" +"PO-Revision-Date: 2013-12-30 20:47+0800\n" +"Last-Translator: chai2010 \n" +"Language-Team: chai2010(团队) \n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-SourceCharset: UTF-8\n" + +msgctxt "main.init" +msgid "Gettext in init." +msgstr "Init函数中的Gettext." + +msgctxt "main.main" +msgid "Hello, world!" +msgstr "你好, 世界!" + +msgctxt "main.func" +msgid "Gettext in func." +msgstr "闭包函数中的Gettext." + +msgctxt "github.com/chai2010/gettext-go/examples/hi.SayHi" +msgid "pkg hi: Hello, world!" +msgstr "来自\"Hi\"包的问候: 你好, 世界!" diff --git a/vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_RESOURCE/hello/poems.txt b/vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_RESOURCE/hello/poems.txt new file mode 100644 index 0000000000..60a5a8ab3c --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/local/zh_CN/LC_RESOURCE/hello/poems.txt @@ -0,0 +1,19 @@ +yuèxiàdúzhuó +月下独酌 +lǐbái +李白 + +huājiānyīhújiǔ,dúzhuówúxiānɡqīn。 +花间一壶酒,独酌无相亲。 +jǔbēiyāomínɡyuè,duìyǐnɡchénɡsānrén。 +举杯邀明月,对影成三人。 +yuèjìbùjiěyǐn,yǐnɡtúsuíwǒshēn。 +月既不解饮,影徒随我身。 +zànbànyuèjiānɡyǐnɡ,xínɡlèxūjíchūn。 +暂伴月将影,行乐须及春。 +wǒɡēyuèpáihuái,wǒwǔyǐnɡlínɡluàn。 +我歌月徘徊,我舞影零乱。 +xǐnɡshítónɡjiāohuān,zuìhòuɡèfēnsàn。 +醒时同交欢,醉后各分散。 +yǒnɡjiéwúqínɡyóu,xiānɡqīmiǎoyúnhàn。 +永结无情游,相期邈云汉。 diff --git a/vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_MESSAGES/hello.mo b/vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_MESSAGES/hello.mo new file mode 100644 index 0000000000000000000000000000000000000000..b7c317bbb57875a1a9b1bab5b7af09e0db97c309 GIT binary patch literal 813 zcmah{OKTKC5FT|@*o)vr5tU9ZDoM}IWDQx{2jb?j3ob6n__#@DwlmW@+r9MkXyVC( z8Uq1O8+;s;fEU60URLlQ_zOJk7#IJ9)tL<&MX>0Ps-nKCuBz_u$L)>e}uq?5$_^)JvhkoNa@mqTL#`unrQ4RDw3FLsR-Iz7`kGtBubNrit8Z^6q|)!gA5U- zG-TLkv^Cg?g<%H`;{fxP^)onh9uUtqpUT*DAJSSmb5X zluDSsdfT7Ji0fmAA6Ii-`(2jY>rujtqmY#>%AYH VZom5(n8Od^ST*, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: gettext-go-examples-hello\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-12-12 20:03+0000\n" +"PO-Revision-Date: 2014-01-01 11:39+0800\n" +"Last-Translator: chai2010 \n" +"Language-Team: chai2010(团队) \n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-SourceCharset: UTF-8\n" + +msgctxt "main.init" +msgid "Gettext in init." +msgstr "Init函數中的Gettext." + +msgctxt "main.main" +msgid "Hello, world!" +msgstr "你好, 世界!" + +msgctxt "main.func" +msgid "Gettext in func." +msgstr "閉包函數中的Gettext." + +msgctxt "github.com/chai2010/gettext-go/examples/hi.SayHi" +msgid "pkg hi: Hello, world!" +msgstr "來自\"Hi\"包的問候: 你好, 世界!" diff --git a/vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_RESOURCE/hello/poems.txt b/vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_RESOURCE/hello/poems.txt new file mode 100644 index 0000000000..c152bd8704 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/examples/local/zh_TW/LC_RESOURCE/hello/poems.txt @@ -0,0 +1,19 @@ +yuèxiàdúzhuó +月下獨酌 +lǐbái +李白 + +huājiānyīhújiǔ,dúzhuówúxiānɡqīn。 +花間一壺酒,獨酌無相親。 +jǔbēiyāomínɡyuè,duìyǐnɡchénɡsānrén。 +舉杯邀明月,對影成三人。 +yuèjìbùjiěyǐn,yǐnɡtúsuíwǒshēn。 +月既不解飲,影徒隨我身。 +zànbànyuèjiānɡyǐnɡ,xínɡlèxūjíchūn。 +暫伴月將影,行樂須及春。 +wǒɡēyuèpáihuái,wǒwǔyǐnɡlínɡluàn。 +我歌月徘徊,我舞影零亂。 +xǐnɡshítónɡjiāohuān,zuìhòuɡèfēnsàn。 +醒時同交歡,醉後各分散。 +yǒnɡjiéwúqínɡyóu,xiānɡqīmiǎoyúnhàn。 +永結無情遊,相期邈雲漢。 diff --git a/vendor/github.com/chai2010/gettext-go/gettext/caller.go b/vendor/github.com/chai2010/gettext-go/gettext/caller.go new file mode 100644 index 0000000000..e24aab3756 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/caller.go @@ -0,0 +1,39 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "regexp" + "runtime" +) + +var ( + reInit = regexp.MustCompile(`init·\d+$`) // main.init·1 + reClosure = regexp.MustCompile(`func·\d+$`) // main.func·001 +) + +// caller types: +// runtime.goexit +// runtime.main +// main.init +// main.main +// main.init·1 -> main.init +// main.func·001 -> main.func +// code.google.com/p/gettext-go/gettext.TestCallerName +// ... +func callerName(skip int) string { + pc, _, _, ok := runtime.Caller(skip) + if !ok { + return "" + } + name := runtime.FuncForPC(pc).Name() + if reInit.MatchString(name) { + return reInit.ReplaceAllString(name, "init") + } + if reClosure.MatchString(name) { + return reClosure.ReplaceAllString(name, "func") + } + return name +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/caller_test.go b/vendor/github.com/chai2010/gettext-go/gettext/caller_test.go new file mode 100644 index 0000000000..9ee0bb97fd --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/caller_test.go @@ -0,0 +1,89 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "testing" +) + +var ( + testInitCallerName0 string = callerName(1) + testInitCallerName1 string + testInitCallerName2 string +) + +func init() { + testInitCallerName1 = callerName(1) +} + +func init() { + testInitCallerName2 = callerName(1) +} + +var tCaller = func(skip int) string { + return callerName(skip + 1) +} + +func TestCallerName(t *testing.T) { + var name string + + // init + name = `github.com/chai2010/gettext-go/gettext.init` + if s := testInitCallerName0; s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + name = `github.com/chai2010/gettext-go/gettext.init` + if s := testInitCallerName1; s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + name = `github.com/chai2010/gettext-go/gettext.init` + if s := testInitCallerName2; s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + + // tInit -> gettext.func + name = `github.com/chai2010/gettext-go/gettext.func` + if s := tCaller(0); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + + // caller stack + name = `github.com/chai2010/gettext-go/gettext.callerName` + if s := callerName(0); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + name = `github.com/chai2010/gettext-go/gettext.TestCallerName` + if s := callerName(1); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + name = `testing.tRunner` + if s := callerName(2); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + name = `runtime.goexit` + if s := callerName(3); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + name = "" + if s := callerName(4); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + + // closure + func() { + name = `github.com/chai2010/gettext-go/gettext.func` + if s := callerName(1); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + }() + func() { + func() { + name = `github.com/chai2010/gettext-go/gettext.func` + if s := callerName(1); s != name { + t.Fatalf("expect = %s, got = %s", name, s) + } + }() + }() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/doc.go b/vendor/github.com/chai2010/gettext-go/gettext/doc.go new file mode 100644 index 0000000000..422bf2c6d7 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/doc.go @@ -0,0 +1,66 @@ +// Copyright 2013 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package gettext implements a basic GNU's gettext library. + +Example: + import ( + "github.com/chai2010/gettext-go/gettext" + ) + + func main() { + gettext.SetLocale("zh_CN") + gettext.Textdomain("hello") + + // gettext.BindTextdomain("hello", "local", nil) // from local dir + // gettext.BindTextdomain("hello", "local.zip", nil) // from local zip file + // gettext.BindTextdomain("hello", "local.zip", zipData) // from embedded zip data + + gettext.BindTextdomain("hello", "local", nil) + + // translate source text + fmt.Println(gettext.Gettext("Hello, world!")) + // Output: 你好, 世界! + + // translate resource + fmt.Println(string(gettext.Getdata("poems.txt"))) + // Output: ... + } + +Translate directory struct("../examples/local.zip"): + + Root: "path" or "file.zip/zipBaseName" + +-default # local: $(LC_MESSAGES) or $(LANG) or "default" + | +-LC_MESSAGES # just for `gettext.Gettext` + | | +-hello.mo # $(Root)/$(local)/LC_MESSAGES/$(domain).mo + | | \-hello.po # $(Root)/$(local)/LC_MESSAGES/$(domain).mo + | | + | \-LC_RESOURCE # just for `gettext.Getdata` + | +-hello # domain map a dir in resource translate + | +-favicon.ico # $(Root)/$(local)/LC_RESOURCE/$(domain)/$(filename) + | \-poems.txt + | + \-zh_CN # simple chinese translate + +-LC_MESSAGES + | +-hello.mo # try "$(domain).mo" first + | \-hello.po # try "$(domain).po" second + | + \-LC_RESOURCE + +-hello + +-favicon.ico # try "$(local)/$(domain)/file" first + \-poems.txt # try "default/$(domain)/file" second + +See: + http://en.wikipedia.org/wiki/Gettext + http://www.gnu.org/software/gettext/manual/html_node + http://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html + http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html + http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html + http://www.poedit.net/ + +Please report bugs to . +Thanks! +*/ +package gettext diff --git a/vendor/github.com/chai2010/gettext-go/gettext/domain.go b/vendor/github.com/chai2010/gettext-go/gettext/domain.go new file mode 100644 index 0000000000..f860b27b6b --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/domain.go @@ -0,0 +1,119 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "sync" +) + +type domainManager struct { + mutex sync.Mutex + locale string + domain string + domainMap map[string]*fileSystem + trTextMap map[string]*translator +} + +func newDomainManager() *domainManager { + return &domainManager{ + locale: DefaultLocale, + domainMap: make(map[string]*fileSystem), + trTextMap: make(map[string]*translator), + } +} + +func (p *domainManager) makeTrMapKey(domain, locale string) string { + return domain + "_$$$_" + locale +} + +func (p *domainManager) Bind(domain, path string, data []byte) (domains, paths []string) { + p.mutex.Lock() + defer p.mutex.Unlock() + + switch { + case domain != "" && path != "": // bind new domain + p.bindDomainTranslators(domain, path, data) + case domain != "" && path == "": // delete domain + p.deleteDomain(domain) + } + + // return all bind domain + for k, fs := range p.domainMap { + domains = append(domains, k) + paths = append(paths, fs.FsName) + } + return +} + +func (p *domainManager) SetLocale(locale string) string { + p.mutex.Lock() + defer p.mutex.Unlock() + if locale != "" { + p.locale = locale + } + return p.locale +} + +func (p *domainManager) SetDomain(domain string) string { + p.mutex.Lock() + defer p.mutex.Unlock() + if domain != "" { + p.domain = domain + } + return p.domain +} + +func (p *domainManager) Getdata(name string) []byte { + return p.getdata(p.domain, name) +} + +func (p *domainManager) DGetdata(domain, name string) []byte { + return p.getdata(domain, name) +} + +func (p *domainManager) PNGettext(msgctxt, msgid, msgidPlural string, n int) string { + p.mutex.Lock() + defer p.mutex.Unlock() + return p.gettext(p.domain, msgctxt, msgid, msgidPlural, n) +} + +func (p *domainManager) DPNGettext(domain, msgctxt, msgid, msgidPlural string, n int) string { + p.mutex.Lock() + defer p.mutex.Unlock() + return p.gettext(domain, msgctxt, msgid, msgidPlural, n) +} + +func (p *domainManager) gettext(domain, msgctxt, msgid, msgidPlural string, n int) string { + if p.locale == "" || p.domain == "" { + return msgid + } + if _, ok := p.domainMap[domain]; !ok { + return msgid + } + if f, ok := p.trTextMap[p.makeTrMapKey(domain, p.locale)]; ok { + return f.PNGettext(msgctxt, msgid, msgidPlural, n) + } + return msgid +} + +func (p *domainManager) getdata(domain, name string) []byte { + if p.locale == "" || p.domain == "" { + return nil + } + if _, ok := p.domainMap[domain]; !ok { + return nil + } + if fs, ok := p.domainMap[domain]; ok { + if data, err := fs.LoadResourceFile(domain, p.locale, name); err == nil { + return data + } + if p.locale != "default" { + if data, err := fs.LoadResourceFile(domain, "default", name); err == nil { + return data + } + } + } + return nil +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/domain_helper.go b/vendor/github.com/chai2010/gettext-go/gettext/domain_helper.go new file mode 100644 index 0000000000..8dce58e655 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/domain_helper.go @@ -0,0 +1,50 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "fmt" + "strings" +) + +func (p *domainManager) bindDomainTranslators(domain, path string, data []byte) { + if _, ok := p.domainMap[domain]; ok { + p.deleteDomain(domain) // delete old domain + } + fs := newFileSystem(path, data) + for locale, _ := range fs.LocaleMap { + trMapKey := p.makeTrMapKey(domain, locale) + if data, err := fs.LoadMessagesFile(domain, locale, ".mo"); err == nil { + p.trTextMap[trMapKey], _ = newMoTranslator( + fmt.Sprintf("%s_%s.mo", domain, locale), + data, + ) + continue + } + if data, err := fs.LoadMessagesFile(domain, locale, ".po"); err == nil { + p.trTextMap[trMapKey], _ = newPoTranslator( + fmt.Sprintf("%s_%s.po", domain, locale), + data, + ) + continue + } + p.trTextMap[p.makeTrMapKey(domain, locale)] = nilTranslator + } + p.domainMap[domain] = fs +} + +func (p *domainManager) deleteDomain(domain string) { + if _, ok := p.domainMap[domain]; !ok { + return + } + // delete all mo files + trMapKeyPrefix := p.makeTrMapKey(domain, "") + for k, _ := range p.trTextMap { + if strings.HasPrefix(k, trMapKeyPrefix) { + delete(p.trTextMap, k) + } + } + delete(p.domainMap, domain) +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/fs.go b/vendor/github.com/chai2010/gettext-go/gettext/fs.go new file mode 100644 index 0000000000..1c2e23c1d0 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/fs.go @@ -0,0 +1,187 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "archive/zip" + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "strings" +) + +type fileSystem struct { + FsName string + FsRoot string + FsZipData []byte + LocaleMap map[string]bool +} + +func newFileSystem(path string, data []byte) *fileSystem { + fs := &fileSystem{ + FsName: path, + FsZipData: data, + } + if err := fs.init(); err != nil { + log.Printf("gettext-go: invalid domain, err = %v", err) + } + return fs +} + +func (p *fileSystem) init() error { + zipName := func(name string) string { + if x := strings.LastIndexAny(name, `\/`); x != -1 { + name = name[x+1:] + } + name = strings.TrimSuffix(name, ".zip") + return name + } + + // zip data + if len(p.FsZipData) != 0 { + p.FsRoot = zipName(p.FsName) + p.LocaleMap = p.lsZip(p.FsZipData) + return nil + } + + // local dir or zip file + fi, err := os.Stat(p.FsName) + if err != nil { + return err + } + + // local dir + if fi.IsDir() { + p.FsRoot = p.FsName + p.LocaleMap = p.lsDir(p.FsName) + return nil + } + + // local zip file + p.FsZipData, err = ioutil.ReadFile(p.FsName) + if err != nil { + return err + } + p.FsRoot = zipName(p.FsName) + p.LocaleMap = p.lsZip(p.FsZipData) + return nil +} + +func (p *fileSystem) LoadMessagesFile(domain, local, ext string) ([]byte, error) { + if len(p.FsZipData) == 0 { + trName := p.makeMessagesFileName(domain, local, ext) + rcData, err := ioutil.ReadFile(trName) + if err != nil { + return nil, err + } + return rcData, nil + } else { + r, err := zip.NewReader(bytes.NewReader(p.FsZipData), int64(len(p.FsZipData))) + if err != nil { + return nil, err + } + + trName := p.makeMessagesFileName(domain, local, ext) + for _, f := range r.File { + if f.Name != trName { + continue + } + rc, err := f.Open() + if err != nil { + return nil, err + } + rcData, err := ioutil.ReadAll(rc) + rc.Close() + return rcData, err + } + return nil, fmt.Errorf("not found") + } +} + +func (p *fileSystem) LoadResourceFile(domain, local, name string) ([]byte, error) { + if len(p.FsZipData) == 0 { + rcName := p.makeResourceFileName(domain, local, name) + rcData, err := ioutil.ReadFile(rcName) + if err != nil { + return nil, err + } + return rcData, nil + } else { + r, err := zip.NewReader(bytes.NewReader(p.FsZipData), int64(len(p.FsZipData))) + if err != nil { + return nil, err + } + + rcName := p.makeResourceFileName(domain, local, name) + for _, f := range r.File { + if f.Name != rcName { + continue + } + rc, err := f.Open() + if err != nil { + return nil, err + } + rcData, err := ioutil.ReadAll(rc) + rc.Close() + return rcData, err + } + return nil, fmt.Errorf("not found") + } +} + +func (p *fileSystem) makeMessagesFileName(domain, local, ext string) string { + return fmt.Sprintf("%s/%s/LC_MESSAGES/%s%s", p.FsRoot, local, domain, ext) +} + +func (p *fileSystem) makeResourceFileName(domain, local, name string) string { + return fmt.Sprintf("%s/%s/LC_RESOURCE/%s/%s", p.FsRoot, local, domain, name) +} + +func (p *fileSystem) lsZip(data []byte) map[string]bool { + r, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + return nil + } + ssMap := make(map[string]bool) + for _, f := range r.File { + if x := strings.Index(f.Name, "LC_MESSAGES"); x != -1 { + s := strings.TrimRight(f.Name[:x], `\/`) + if x = strings.LastIndexAny(s, `\/`); x != -1 { + s = s[x+1:] + } + if s != "" { + ssMap[s] = true + } + continue + } + if x := strings.Index(f.Name, "LC_RESOURCE"); x != -1 { + s := strings.TrimRight(f.Name[:x], `\/`) + if x = strings.LastIndexAny(s, `\/`); x != -1 { + s = s[x+1:] + } + if s != "" { + ssMap[s] = true + } + continue + } + } + return ssMap +} + +func (p *fileSystem) lsDir(path string) map[string]bool { + list, err := ioutil.ReadDir(path) + if err != nil { + return nil + } + ssMap := make(map[string]bool) + for _, dir := range list { + if dir.IsDir() { + ssMap[dir.Name()] = true + } + } + return ssMap +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/gettext.go b/vendor/github.com/chai2010/gettext-go/gettext/gettext.go new file mode 100644 index 0000000000..ca14065b22 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/gettext.go @@ -0,0 +1,184 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +var ( + defaultManager = newDomainManager() +) + +var ( + DefaultLocale = getDefaultLocale() // use $(LC_MESSAGES) or $(LANG) or "default" +) + +// SetLocale sets and queries the program's current locale. +// +// If the locale is not empty string, set the new local. +// +// If the locale is empty string, don't change anything. +// +// Returns is the current locale. +// +// Examples: +// SetLocale("") // get locale: return DefaultLocale +// SetLocale("zh_CN") // set locale: return zh_CN +// SetLocale("") // get locale: return zh_CN +func SetLocale(locale string) string { + return defaultManager.SetLocale(locale) +} + +// BindTextdomain sets and queries program's domains. +// +// If the domain and path are all not empty string, bind the new domain. +// If the domain already exists, return error. +// +// If the domain is not empty string, but the path is the empty string, +// delete the domain. +// If the domain don't exists, return error. +// +// If the domain and the path are all empty string, don't change anything. +// +// Returns is the all bind domains. +// +// Examples: +// BindTextdomain("poedit", "local", nil) // bind "poedit" domain +// BindTextdomain("", "", nil) // return all domains +// BindTextdomain("poedit", "", nil) // delete "poedit" domain +// BindTextdomain("", "", nil) // return all domains +// +// Use zip file: +// BindTextdomain("poedit", "local.zip", nil) // bind "poedit" domain +// BindTextdomain("poedit", "local.zip", zipData) // bind "poedit" domain +// +func BindTextdomain(domain, path string, zipData []byte) (domains, paths []string) { + return defaultManager.Bind(domain, path, zipData) +} + +// Textdomain sets and retrieves the current message domain. +// +// If the domain is not empty string, set the new domains. +// +// If the domain is empty string, don't change anything. +// +// Returns is the all used domains. +// +// Examples: +// Textdomain("poedit") // set domain: poedit +// Textdomain("") // get domain: return poedit +func Textdomain(domain string) string { + return defaultManager.SetDomain(domain) +} + +// Gettext attempt to translate a text string into the user's native language, +// by looking up the translation in a message catalog. +// +// It use the caller's function name as the msgctxt. +// +// Examples: +// func Foo() { +// msg := gettext.Gettext("Hello") // msgctxt is "some/package/name.Foo" +// } +func Gettext(msgid string) string { + return PGettext(callerName(2), msgid) +} + +// Getdata attempt to translate a resource file into the user's native language, +// by looking up the translation in a message catalog. +// +// Examples: +// func Foo() { +// Textdomain("hello") +// BindTextdomain("hello", "local.zip", nilOrZipData) +// poems := gettext.Getdata("poems.txt") +// } +func Getdata(name string) []byte { + return defaultManager.Getdata(name) +} + +// NGettext attempt to translate a text string into the user's native language, +// by looking up the appropriate plural form of the translation in a message +// catalog. +// +// It use the caller's function name as the msgctxt. +// +// Examples: +// func Foo() { +// msg := gettext.NGettext("%d people", "%d peoples", 2) +// } +func NGettext(msgid, msgidPlural string, n int) string { + return PNGettext(callerName(2), msgid, msgidPlural, n) +} + +// PGettext attempt to translate a text string into the user's native language, +// by looking up the translation in a message catalog. +// +// Examples: +// func Foo() { +// msg := gettext.PGettext("gettext-go.example", "Hello") // msgctxt is "gettext-go.example" +// } +func PGettext(msgctxt, msgid string) string { + return PNGettext(msgctxt, msgid, "", 0) +} + +// PNGettext attempt to translate a text string into the user's native language, +// by looking up the appropriate plural form of the translation in a message +// catalog. +// +// Examples: +// func Foo() { +// msg := gettext.PNGettext("gettext-go.example", "%d people", "%d peoples", 2) +// } +func PNGettext(msgctxt, msgid, msgidPlural string, n int) string { + return defaultManager.PNGettext(msgctxt, msgid, msgidPlural, n) +} + +// DGettext like Gettext(), but looking up the message in the specified domain. +// +// Examples: +// func Foo() { +// msg := gettext.DGettext("poedit", "Hello") +// } +func DGettext(domain, msgid string) string { + return DPGettext(domain, callerName(2), msgid) +} + +// DNGettext like NGettext(), but looking up the message in the specified domain. +// +// Examples: +// func Foo() { +// msg := gettext.PNGettext("poedit", "gettext-go.example", "%d people", "%d peoples", 2) +// } +func DNGettext(domain, msgid, msgidPlural string, n int) string { + return DPNGettext(domain, callerName(2), msgid, msgidPlural, n) +} + +// DPGettext like PGettext(), but looking up the message in the specified domain. +// +// Examples: +// func Foo() { +// msg := gettext.DPGettext("poedit", "gettext-go.example", "Hello") +// } +func DPGettext(domain, msgctxt, msgid string) string { + return DPNGettext(domain, msgctxt, msgid, "", 0) +} + +// DPNGettext like PNGettext(), but looking up the message in the specified domain. +// +// Examples: +// func Foo() { +// msg := gettext.DPNGettext("poedit", "gettext-go.example", "%d people", "%d peoples", 2) +// } +func DPNGettext(domain, msgctxt, msgid, msgidPlural string, n int) string { + return defaultManager.DPNGettext(domain, msgctxt, msgid, msgidPlural, n) +} + +// DGetdata like Getdata(), but looking up the resource in the specified domain. +// +// Examples: +// func Foo() { +// msg := gettext.DGetdata("hello", "poems.txt") +// } +func DGetdata(domain, name string) []byte { + return defaultManager.DGetdata(domain, name) +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/gettext_test.go b/vendor/github.com/chai2010/gettext-go/gettext/gettext_test.go new file mode 100644 index 0000000000..2281ef4649 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/gettext_test.go @@ -0,0 +1,253 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "io/ioutil" + "strings" + "testing" +) + +var testZipData = func() []byte { + if data, err := ioutil.ReadFile("../examples/local.zip"); err == nil { + return data + } + return nil +}() + +func TestGettext(t *testing.T) { + Textdomain("hello") + + // local file system + BindTextdomain("hello", "../examples/local", nil) + testGettext(t, true) + BindTextdomain("hello", "", nil) + testGettext(t, false) + + // local zip file system + BindTextdomain("hello", "../examples/local.zip", nil) + testGettext(t, true) + BindTextdomain("hello", "", nil) + testGettext(t, false) + + // embedded zip file system + BindTextdomain("hello", "local.zip", testZipData) + testGettext(t, true) + BindTextdomain("hello", "", nil) + testGettext(t, false) +} + +func TestGetdata(t *testing.T) { + Textdomain("hello") + + // local file system + BindTextdomain("hello", "../examples/local", nil) + testGetdata(t, true) + BindTextdomain("hello", "", nil) + testGetdata(t, false) + + // local zip file system + BindTextdomain("hello", "../examples/local.zip", nil) + testGetdata(t, true) + BindTextdomain("hello", "", nil) + testGetdata(t, false) + + // embedded zip file system + BindTextdomain("hello", "local.zip", testZipData) + testGetdata(t, true) + BindTextdomain("hello", "", nil) + testGetdata(t, false) +} + +func testGettext(t *testing.T, hasTransle bool) { + for i, v := range testTexts { + if lang := SetLocale(v.lang); lang != v.lang { + t.Fatalf("%d: expect = %s, got = %v", i, v.lang, lang) + } + if hasTransle { + if dst := PGettext(v.ctx, v.src); dst != v.dst { + t.Fatalf("%d: expect = %q, got = %q", i, v.dst, dst) + } + } else { + if dst := PGettext(v.ctx, v.src); dst != v.src { + t.Fatalf("%d: expect = %s, got = %v", i, v.src, dst) + } + } + } +} + +func testGetdata(t *testing.T, hasTransle bool) { + for i, v := range testResources { + if lang := SetLocale(v.lang); lang != v.lang { + t.Fatalf("%d: expect = %s, got = %v", i, v.lang, lang) + } + if hasTransle { + v.data = strings.Replace(v.data, "\r", "", -1) + data := strings.Replace(string(Getdata(v.path)), "\r", "", -1) + if data != v.data { + t.Fatalf("%d: expect = %q, got = %q", i, v.data, data) + } + } else { + if data := string(Getdata(v.path)); data != "" { + t.Fatalf("%d: expect = %s, got = %v", i, "", data) + } + } + } +} + +func BenchmarkGettext(b *testing.B) { + SetLocale("zh_CN") + BindTextdomain("hello", "../examples/local", nil) + Textdomain("hello") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + PGettext(testTexts[0].ctx, testTexts[0].src) + } +} +func BenchmarkGettext_Zip(b *testing.B) { + SetLocale("zh_CN") + BindTextdomain("hello", "../examples/local.zip", nil) + Textdomain("hello") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + PGettext(testTexts[0].ctx, testTexts[0].src) + } +} + +func BenchmarkGetdata(b *testing.B) { + SetLocale("zh_CN") + BindTextdomain("hello", "../examples/local", nil) + Textdomain("hello") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Getdata(testResources[0].path) + } +} +func BenchmarkGetdata_Zip(b *testing.B) { + SetLocale("zh_CN") + BindTextdomain("hello", "../examples/local.zip", nil) + Textdomain("hello") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Getdata(testResources[0].path) + } +} + +var testTexts = []struct { + lang string + ctx string + src string + dst string +}{ + // default + {"default", "main.init", "Gettext in init.", "Gettext in init."}, + {"default", "main.main", "Hello, world!", "Hello, world!"}, + {"default", "main.func", "Gettext in func.", "Gettext in func."}, + {"default", "github.com/chai2010/gettext-go/examples/hi.SayHi", "pkg hi: Hello, world!", "pkg hi: Hello, world!"}, + + // zh_CN + {"zh_CN", "main.init", "Gettext in init.", "Init函数中的Gettext."}, + {"zh_CN", "main.main", "Hello, world!", "你好, 世界!"}, + {"zh_CN", "main.func", "Gettext in func.", "闭包函数中的Gettext."}, + {"zh_CN", "github.com/chai2010/gettext-go/examples/hi.SayHi", "pkg hi: Hello, world!", "来自\"Hi\"包的问候: 你好, 世界!"}, + + // zh_TW + {"zh_TW", "main.init", "Gettext in init.", "Init函數中的Gettext."}, + {"zh_TW", "main.main", "Hello, world!", "你好, 世界!"}, + {"zh_TW", "main.func", "Gettext in func.", "閉包函數中的Gettext."}, + {"zh_TW", "github.com/chai2010/gettext-go/examples/hi.SayHi", "pkg hi: Hello, world!", "來自\"Hi\"包的問候: 你好, 世界!"}, +} + +var testResources = []struct { + lang string + path string + data string +}{ + // default + { + "default", + "poems.txt", + `Drinking Alone Under the Moon +Li Bai + +flowers among one jar liquor +alone carouse without mutual intimate + +raise cup greet bright moon +facing shadow become three persons + +moon since not free to-drink +shadow follow accompany my body + +briefly accompany moon with shadow +go happy should avail-oneself-of spring + +my song moon walk-to-and-fro irresolute +my dance shadow fragments disorderly + +sober time together mix glad +drunk after each divide scatter + +eternal connect without consciouness-of-self roam +mutual appointment remote cloud Milky-Way +`, + }, + + // zh_CN + { + "zh_CN", + "poems.txt", + `yuèxiàdúzhuó +月下独酌 +lǐbái +李白 + +huājiānyīhújiǔ,dúzhuówúxiānɡqīn。 +花间一壶酒,独酌无相亲。 +jǔbēiyāomínɡyuè,duìyǐnɡchénɡsānrén。 +举杯邀明月,对影成三人。 +yuèjìbùjiěyǐn,yǐnɡtúsuíwǒshēn。 +月既不解饮,影徒随我身。 +zànbànyuèjiānɡyǐnɡ,xínɡlèxūjíchūn。 +暂伴月将影,行乐须及春。 +wǒɡēyuèpáihuái,wǒwǔyǐnɡlínɡluàn。 +我歌月徘徊,我舞影零乱。 +xǐnɡshítónɡjiāohuān,zuìhòuɡèfēnsàn。 +醒时同交欢,醉后各分散。 +yǒnɡjiéwúqínɡyóu,xiānɡqīmiǎoyúnhàn。 +永结无情游,相期邈云汉。 +`, + }, + + // zh_TW + { + "zh_TW", + "poems.txt", + `yuèxiàdúzhuó +月下獨酌 +lǐbái +李白 + +huājiānyīhújiǔ,dúzhuówúxiānɡqīn。 +花間一壺酒,獨酌無相親。 +jǔbēiyāomínɡyuè,duìyǐnɡchénɡsānrén。 +舉杯邀明月,對影成三人。 +yuèjìbùjiěyǐn,yǐnɡtúsuíwǒshēn。 +月既不解飲,影徒隨我身。 +zànbànyuèjiānɡyǐnɡ,xínɡlèxūjíchūn。 +暫伴月將影,行樂須及春。 +wǒɡēyuèpáihuái,wǒwǔyǐnɡlínɡluàn。 +我歌月徘徊,我舞影零亂。 +xǐnɡshítónɡjiāohuān,zuìhòuɡèfēnsàn。 +醒時同交歡,醉後各分散。 +yǒnɡjiéwúqínɡyóu,xiānɡqīmiǎoyúnhàn。 +永結無情遊,相期邈雲漢。 +`, + }, +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/hello.go b/vendor/github.com/chai2010/gettext-go/gettext/hello.go new file mode 100644 index 0000000000..372ccf39c8 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/hello.go @@ -0,0 +1,22 @@ +// Copyright 2013 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "fmt" + + "github.com/chai2010/gettext-go" +) + +func main() { + gettext.SetLocale("zh_CN") + gettext.BindTextdomain("hello", "../examples/local", nil) + gettext.Textdomain("hello") + + fmt.Println(gettext.Gettext("Hello, world!")) + // Output: 你好, 世界! +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/local.go b/vendor/github.com/chai2010/gettext-go/gettext/local.go new file mode 100644 index 0000000000..179a392fe2 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/local.go @@ -0,0 +1,34 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "os" + "strings" +) + +func getDefaultLocale() string { + if v := os.Getenv("LC_MESSAGES"); v != "" { + return simplifiedLocale(v) + } + if v := os.Getenv("LANG"); v != "" { + return simplifiedLocale(v) + } + return "default" +} + +func simplifiedLocale(lang string) string { + // en_US/en_US.UTF-8/zh_CN/zh_TW/el_GR@euro/... + if idx := strings.Index(lang, ":"); idx != -1 { + lang = lang[:idx] + } + if idx := strings.Index(lang, "@"); idx != -1 { + lang = lang[:idx] + } + if idx := strings.Index(lang, "."); idx != -1 { + lang = lang[:idx] + } + return strings.TrimSpace(lang) +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/doc.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/doc.go new file mode 100644 index 0000000000..9677680631 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/doc.go @@ -0,0 +1,74 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mo provides support for reading and writing GNU MO file. + +Examples: + import ( + "github.com/chai2010/gettext-go/gettext/mo" + ) + + func main() { + moFile, err := mo.Load("test.mo") + if err != nil { + log.Fatal(err) + } + fmt.Printf("%v", moFile) + } + +GNU MO file struct: + + byte + +------------------------------------------+ + 0 | magic number = 0x950412de | + | | + 4 | file format revision = 0 | + | | + 8 | number of strings | == N + | | + 12 | offset of table with original strings | == O + | | + 16 | offset of table with translation strings | == T + | | + 20 | size of hashing table | == S + | | + 24 | offset of hashing table | == H + | | + . . + . (possibly more entries later) . + . . + | | + O | length & offset 0th string ----------------. + O + 8 | length & offset 1st string ------------------. + ... ... | | + O + ((N-1)*8)| length & offset (N-1)th string | | | + | | | | + T | length & offset 0th translation ---------------. + T + 8 | length & offset 1st translation -----------------. + ... ... | | | | + T + ((N-1)*8)| length & offset (N-1)th translation | | | | | + | | | | | | + H | start hash table | | | | | + ... ... | | | | + H + S * 4 | end hash table | | | | | + | | | | | | + | NUL terminated 0th string <----------------' | | | + | | | | | + | NUL terminated 1st string <------------------' | | + | | | | + ... ... | | + | | | | + | NUL terminated 0th translation <---------------' | + | | | + | NUL terminated 1st translation <-----------------' + | | + ... ... + | | + +------------------------------------------+ + +The GNU MO file specification is at +http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html. +*/ +package mo diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/encoder.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/encoder.go new file mode 100644 index 0000000000..9b1c240b4f --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/encoder.go @@ -0,0 +1,124 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "bytes" + "encoding/binary" + "sort" + "strings" +) + +type moHeader struct { + MagicNumber uint32 + MajorVersion uint16 + MinorVersion uint16 + MsgIdCount uint32 + MsgIdOffset uint32 + MsgStrOffset uint32 + HashSize uint32 + HashOffset uint32 +} + +type moStrPos struct { + Size uint32 // must keep fields order + Addr uint32 +} + +func encodeFile(f *File) []byte { + hdr := &moHeader{ + MagicNumber: MoMagicLittleEndian, + } + data := encodeData(hdr, f) + data = append(encodeHeader(hdr), data...) + return data +} + +// encode data and init moHeader +func encodeData(hdr *moHeader, f *File) []byte { + msgList := []Message{f.MimeHeader.toMessage()} + for _, v := range f.Messages { + if len(v.MsgId) == 0 { + continue + } + if len(v.MsgStr) == 0 && len(v.MsgStrPlural) == 0 { + continue + } + msgList = append(msgList, v) + } + sort.Sort(byMessages(msgList)) + + var buf bytes.Buffer + var msgIdPosList = make([]moStrPos, len(msgList)) + var msgStrPosList = make([]moStrPos, len(msgList)) + for i, v := range msgList { + // write msgid + msgId := encodeMsgId(v) + msgIdPosList[i].Addr = uint32(buf.Len() + MoHeaderSize) + msgIdPosList[i].Size = uint32(len(msgId)) + buf.WriteString(msgId) + // write msgstr + msgStr := encodeMsgStr(v) + msgStrPosList[i].Addr = uint32(buf.Len() + MoHeaderSize) + msgStrPosList[i].Size = uint32(len(msgStr)) + buf.WriteString(msgStr) + } + + hdr.MsgIdOffset = uint32(buf.Len() + MoHeaderSize) + binary.Write(&buf, binary.LittleEndian, msgIdPosList) + hdr.MsgStrOffset = uint32(buf.Len() + MoHeaderSize) + binary.Write(&buf, binary.LittleEndian, msgStrPosList) + + hdr.MsgIdCount = uint32(len(msgList)) + return buf.Bytes() +} + +// must called after encodeData +func encodeHeader(hdr *moHeader) []byte { + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, hdr) + return buf.Bytes() +} + +func encodeMsgId(v Message) string { + if v.MsgContext != "" && v.MsgIdPlural != "" { + return v.MsgContext + EotSeparator + v.MsgId + NulSeparator + v.MsgIdPlural + } + if v.MsgContext != "" && v.MsgIdPlural == "" { + return v.MsgContext + EotSeparator + v.MsgId + } + if v.MsgContext == "" && v.MsgIdPlural != "" { + return v.MsgId + NulSeparator + v.MsgIdPlural + } + return v.MsgId +} + +func encodeMsgStr(v Message) string { + if v.MsgIdPlural != "" { + return strings.Join(v.MsgStrPlural, NulSeparator) + } + return v.MsgStr +} + +type byMessages []Message + +func (d byMessages) Len() int { + return len(d) +} +func (d byMessages) Less(i, j int) bool { + if a, b := d[i].MsgContext, d[j].MsgContext; a != b { + return a < b + } + if a, b := d[i].MsgId, d[j].MsgId; a != b { + return a < b + } + if a, b := d[i].MsgIdPlural, d[j].MsgIdPlural; a != b { + return a < b + } + return false +} +func (d byMessages) Swap(i, j int) { + d[i], d[j] = d[j], d[i] +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/encoder_test.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/encoder_test.go new file mode 100644 index 0000000000..e650f86bd2 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/encoder_test.go @@ -0,0 +1,55 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "reflect" + "sort" + "testing" +) + +func TestFile_Data(t *testing.T) { + f, err := LoadData(testMoFile.Data()) + if err != nil { + t.Fatal(err) + } + if a, b := len(f.Messages), len(testMoFile.Messages); a != b { + t.Logf("size not equal: expect = %d, got = %d", b, a) + } + for i, v := range f.Messages { + if !reflect.DeepEqual(&v, &testMoFile.Messages[i]) { + t.Fatalf("%d: expect = %v, got = %v", i, testMoFile.Messages[i], v) + } + } +} + +func init() { + sort.Sort(byMessages(testMoFile.Messages)) +} + +var testMoFile = &File{ + Messages: []Message{ + Message{ + MsgContext: "main.init", + MsgId: "Gettext in init.", + MsgStr: "Init函数中的Gettext.", + }, + Message{ + MsgContext: "main.main", + MsgId: "Hello, world!", + MsgStr: "你好, 世界!", + }, + Message{ + MsgContext: "main.func", + MsgId: "Gettext in func.", + MsgStr: "闭包函数中的Gettext.", + }, + Message{ + MsgContext: "code.google.com/p/gettext-go/examples/hi.SayHi", + MsgId: "pkg hi: Hello, world!", + MsgStr: "来自\"Hi\"包的问候: 你好, 世界!", + }, + }, +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/file.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/file.go new file mode 100644 index 0000000000..b49a77b42a --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/file.go @@ -0,0 +1,193 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "bytes" + "encoding/binary" + "fmt" + "io/ioutil" + "strings" +) + +const ( + MoHeaderSize = 28 + MoMagicLittleEndian = 0x950412de + MoMagicBigEndian = 0xde120495 + + EotSeparator = "\x04" // msgctxt and msgid separator + NulSeparator = "\x00" // msgid and msgstr separator +) + +// File represents an MO File. +// +// See http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html +type File struct { + MagicNumber uint32 + MajorVersion uint16 + MinorVersion uint16 + MsgIdCount uint32 + MsgIdOffset uint32 + MsgStrOffset uint32 + HashSize uint32 + HashOffset uint32 + MimeHeader Header + Messages []Message +} + +// Load loads a named mo file. +func Load(name string) (*File, error) { + data, err := ioutil.ReadFile(name) + if err != nil { + return nil, err + } + return LoadData(data) +} + +// LoadData loads mo file format data. +func LoadData(data []byte) (*File, error) { + r := bytes.NewReader(data) + + var magicNumber uint32 + if err := binary.Read(r, binary.LittleEndian, &magicNumber); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + var bo binary.ByteOrder + switch magicNumber { + case MoMagicLittleEndian: + bo = binary.LittleEndian + case MoMagicBigEndian: + bo = binary.BigEndian + default: + return nil, fmt.Errorf("gettext: %v", "invalid magic number") + } + + var header struct { + MajorVersion uint16 + MinorVersion uint16 + MsgIdCount uint32 + MsgIdOffset uint32 + MsgStrOffset uint32 + HashSize uint32 + HashOffset uint32 + } + if err := binary.Read(r, bo, &header); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + if v := header.MajorVersion; v != 0 && v != 1 { + return nil, fmt.Errorf("gettext: %v", "invalid version number") + } + if v := header.MinorVersion; v != 0 && v != 1 { + return nil, fmt.Errorf("gettext: %v", "invalid version number") + } + + msgIdStart := make([]uint32, header.MsgIdCount) + msgIdLen := make([]uint32, header.MsgIdCount) + if _, err := r.Seek(int64(header.MsgIdOffset), 0); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + for i := 0; i < int(header.MsgIdCount); i++ { + if err := binary.Read(r, bo, &msgIdLen[i]); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + if err := binary.Read(r, bo, &msgIdStart[i]); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + } + + msgStrStart := make([]int32, header.MsgIdCount) + msgStrLen := make([]int32, header.MsgIdCount) + if _, err := r.Seek(int64(header.MsgStrOffset), 0); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + for i := 0; i < int(header.MsgIdCount); i++ { + if err := binary.Read(r, bo, &msgStrLen[i]); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + if err := binary.Read(r, bo, &msgStrStart[i]); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + } + + file := &File{ + MagicNumber: magicNumber, + MajorVersion: header.MajorVersion, + MinorVersion: header.MinorVersion, + MsgIdCount: header.MsgIdCount, + MsgIdOffset: header.MsgIdOffset, + MsgStrOffset: header.MsgStrOffset, + HashSize: header.HashSize, + HashOffset: header.HashOffset, + } + for i := 0; i < int(header.MsgIdCount); i++ { + if _, err := r.Seek(int64(msgIdStart[i]), 0); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + msgIdData := make([]byte, msgIdLen[i]) + if _, err := r.Read(msgIdData); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + + if _, err := r.Seek(int64(msgStrStart[i]), 0); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + msgStrData := make([]byte, msgStrLen[i]) + if _, err := r.Read(msgStrData); err != nil { + return nil, fmt.Errorf("gettext: %v", err) + } + + if len(msgIdData) == 0 { + var msg = Message{ + MsgId: string(msgIdData), + MsgStr: string(msgStrData), + } + file.MimeHeader.fromMessage(&msg) + } else { + var msg = Message{ + MsgId: string(msgIdData), + MsgStr: string(msgStrData), + } + // Is this a context message? + if idx := strings.Index(msg.MsgId, EotSeparator); idx != -1 { + msg.MsgContext, msg.MsgId = msg.MsgId[:idx], msg.MsgId[idx+1:] + } + // Is this a plural message? + if idx := strings.Index(msg.MsgId, NulSeparator); idx != -1 { + msg.MsgId, msg.MsgIdPlural = msg.MsgId[:idx], msg.MsgId[idx+1:] + msg.MsgStrPlural = strings.Split(msg.MsgStr, NulSeparator) + msg.MsgStr = "" + } + file.Messages = append(file.Messages, msg) + } + } + + return file, nil +} + +// Save saves a mo file. +func (f *File) Save(name string) error { + return ioutil.WriteFile(name, f.Data(), 0666) +} + +// Save returns a mo file format data. +func (f *File) Data() []byte { + return encodeFile(f) +} + +// String returns the po format file string. +func (f *File) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "# version: %d.%d\n", f.MajorVersion, f.MinorVersion) + fmt.Fprintf(&buf, "%s\n", f.MimeHeader.String()) + fmt.Fprintf(&buf, "\n") + + for k, v := range f.Messages { + fmt.Fprintf(&buf, `msgid "%v"`+"\n", k) + fmt.Fprintf(&buf, `msgstr "%s"`+"\n", v.MsgStr) + fmt.Fprintf(&buf, "\n") + } + + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/file_test.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/file_test.go new file mode 100644 index 0000000000..46f2375052 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/file_test.go @@ -0,0 +1,13 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "testing" +) + +func TestFile(t *testing.T) { + // +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/header.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/header.go new file mode 100644 index 0000000000..d8c7a5e3a3 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/header.go @@ -0,0 +1,109 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "bytes" + "fmt" + "strings" +) + +// Header is the initial comments "SOME DESCRIPTIVE TITLE", "YEAR" +// and "FIRST AUTHOR , YEAR" ought to be replaced by sensible information. +// +// See http://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html#Header-Entry +type Header struct { + ProjectIdVersion string // Project-Id-Version: PACKAGE VERSION + ReportMsgidBugsTo string // Report-Msgid-Bugs-To: FIRST AUTHOR + POTCreationDate string // POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE + PORevisionDate string // PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE + LastTranslator string // Last-Translator: FIRST AUTHOR + LanguageTeam string // Language-Team: golang-china + Language string // Language: zh_CN + MimeVersion string // MIME-Version: 1.0 + ContentType string // Content-Type: text/plain; charset=UTF-8 + ContentTransferEncoding string // Content-Transfer-Encoding: 8bit + PluralForms string // Plural-Forms: nplurals=2; plural=n == 1 ? 0 : 1; + XGenerator string // X-Generator: Poedit 1.5.5 + UnknowFields map[string]string +} + +func (p *Header) fromMessage(msg *Message) { + if msg.MsgId != "" || msg.MsgStr == "" { + return + } + lines := strings.Split(msg.MsgStr, "\n") + for i := 0; i < len(lines); i++ { + idx := strings.Index(lines[i], ":") + if idx < 0 { + continue + } + key := strings.TrimSpace(lines[i][:idx]) + val := strings.TrimSpace(lines[i][idx+1:]) + switch strings.ToUpper(key) { + case strings.ToUpper("Project-Id-Version"): + p.ProjectIdVersion = val + case strings.ToUpper("Report-Msgid-Bugs-To"): + p.ReportMsgidBugsTo = val + case strings.ToUpper("POT-Creation-Date"): + p.POTCreationDate = val + case strings.ToUpper("PO-Revision-Date"): + p.PORevisionDate = val + case strings.ToUpper("Last-Translator"): + p.LastTranslator = val + case strings.ToUpper("Language-Team"): + p.LanguageTeam = val + case strings.ToUpper("Language"): + p.Language = val + case strings.ToUpper("MIME-Version"): + p.MimeVersion = val + case strings.ToUpper("Content-Type"): + p.ContentType = val + case strings.ToUpper("Content-Transfer-Encoding"): + p.ContentTransferEncoding = val + case strings.ToUpper("Plural-Forms"): + p.PluralForms = val + case strings.ToUpper("X-Generator"): + p.XGenerator = val + default: + if p.UnknowFields == nil { + p.UnknowFields = make(map[string]string) + } + p.UnknowFields[key] = val + } + } +} + +func (p *Header) toMessage() Message { + return Message{ + MsgStr: p.String(), + } +} + +// String returns the po format header string. +func (p Header) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, `msgid ""`+"\n") + fmt.Fprintf(&buf, `msgstr ""`+"\n") + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Project-Id-Version", p.ProjectIdVersion) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Report-Msgid-Bugs-To", p.ReportMsgidBugsTo) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "POT-Creation-Date", p.POTCreationDate) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "PO-Revision-Date", p.PORevisionDate) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Last-Translator", p.LastTranslator) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Language-Team", p.LanguageTeam) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Language", p.Language) + if p.MimeVersion != "" { + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "MIME-Version", p.MimeVersion) + } + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Content-Type", p.ContentType) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Content-Transfer-Encoding", p.ContentTransferEncoding) + if p.XGenerator != "" { + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "X-Generator", p.XGenerator) + } + for k, v := range p.UnknowFields { + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", k, v) + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/header_test.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/header_test.go new file mode 100644 index 0000000000..770b6ab5e4 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/header_test.go @@ -0,0 +1,13 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "testing" +) + +func TestHeader(t *testing.T) { + // +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/message.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/message.go new file mode 100644 index 0000000000..91ad79bece --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/message.go @@ -0,0 +1,39 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "bytes" + "fmt" +) + +// A MO file is made up of many entries, +// each entry holding the relation between an original untranslated string +// and its corresponding translation. +// +// See http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html +type Message struct { + MsgContext string // msgctxt context + MsgId string // msgid untranslated-string + MsgIdPlural string // msgid_plural untranslated-string-plural + MsgStr string // msgstr translated-string + MsgStrPlural []string // msgstr[0] translated-string-case-0 +} + +// String returns the po format entry string. +func (p Message) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "msgid %s", encodePoString(p.MsgId)) + if p.MsgIdPlural != "" { + fmt.Fprintf(&buf, "msgid_plural %s", encodePoString(p.MsgIdPlural)) + } + if p.MsgStr != "" { + fmt.Fprintf(&buf, "msgstr %s", encodePoString(p.MsgStr)) + } + for i := 0; i < len(p.MsgStrPlural); i++ { + fmt.Fprintf(&buf, "msgstr[%d] %s", i, encodePoString(p.MsgStrPlural[i])) + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/util.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/util.go new file mode 100644 index 0000000000..3804511053 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/util.go @@ -0,0 +1,110 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "bytes" + "strings" +) + +func decodePoString(text string) string { + lines := strings.Split(text, "\n") + for i := 0; i < len(lines); i++ { + left := strings.Index(lines[i], `"`) + right := strings.LastIndex(lines[i], `"`) + if left < 0 || right < 0 || left == right { + lines[i] = "" + continue + } + line := lines[i][left+1 : right] + data := make([]byte, 0, len(line)) + for i := 0; i < len(line); i++ { + if line[i] != '\\' { + data = append(data, line[i]) + continue + } + if i+1 >= len(line) { + break + } + switch line[i+1] { + case 'n': // \\n -> \n + data = append(data, '\n') + i++ + case 't': // \\t -> \n + data = append(data, '\t') + i++ + case '\\': // \\\ -> ? + data = append(data, '\\') + i++ + } + } + lines[i] = string(data) + } + return strings.Join(lines, "") +} + +func encodePoString(text string) string { + var buf bytes.Buffer + lines := strings.Split(text, "\n") + for i := 0; i < len(lines); i++ { + if lines[i] == "" { + if i != len(lines)-1 { + buf.WriteString(`"\n"` + "\n") + } + continue + } + buf.WriteRune('"') + for _, r := range lines[i] { + switch r { + case '\\': + buf.WriteString(`\\`) + case '"': + buf.WriteString(`\"`) + case '\n': + buf.WriteString(`\n`) + case '\t': + buf.WriteString(`\t`) + default: + buf.WriteRune(r) + } + } + buf.WriteString(`\n"` + "\n") + } + return buf.String() +} + +func encodeCommentPoString(text string) string { + var buf bytes.Buffer + lines := strings.Split(text, "\n") + if len(lines) > 1 { + buf.WriteString(`""` + "\n") + } + for i := 0; i < len(lines); i++ { + if len(lines) > 0 { + buf.WriteString("#| ") + } + buf.WriteRune('"') + for _, r := range lines[i] { + switch r { + case '\\': + buf.WriteString(`\\`) + case '"': + buf.WriteString(`\"`) + case '\n': + buf.WriteString(`\n`) + case '\t': + buf.WriteString(`\t`) + default: + buf.WriteRune(r) + } + } + if i < len(lines)-1 { + buf.WriteString(`\n"` + "\n") + } else { + buf.WriteString(`"`) + } + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/mo/util_test.go b/vendor/github.com/chai2010/gettext-go/gettext/mo/util_test.go new file mode 100644 index 0000000000..626a5a67b6 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/mo/util_test.go @@ -0,0 +1,68 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mo + +import ( + "testing" +) + +func TestDecodePoString(t *testing.T) { + if s := decodePoString(poStrEncode); s != poStrDecode { + t.Fatalf(`expect = %s got = %s`, poStrDecode, s) + } +} + +func TestEncodePoString(t *testing.T) { + if s := encodePoString(poStrDecode); s != poStrEncodeStd { + t.Fatalf(`expect = %s; got = %s`, poStrEncodeStd, s) + } +} + +const poStrEncode = `# noise +123456789 +"Project-Id-Version: Poedit 1.5\n" +"Report-Msgid-Bugs-To: poedit@googlegroups.com\n" +"POT-Creation-Date: 2012-07-30 10:34+0200\n" +"PO-Revision-Date: 2013-02-24 21:00+0800\n" +"Last-Translator: Christopher Meng \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.5.5\n" +"TestPoString: abc" +"123\n" +>> +123456??? +` + +const poStrEncodeStd = `"Project-Id-Version: Poedit 1.5\n" +"Report-Msgid-Bugs-To: poedit@googlegroups.com\n" +"POT-Creation-Date: 2012-07-30 10:34+0200\n" +"PO-Revision-Date: 2013-02-24 21:00+0800\n" +"Last-Translator: Christopher Meng \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.5.5\n" +"TestPoString: abc123\n" +` + +const poStrDecode = `Project-Id-Version: Poedit 1.5 +Report-Msgid-Bugs-To: poedit@googlegroups.com +POT-Creation-Date: 2012-07-30 10:34+0200 +PO-Revision-Date: 2013-02-24 21:00+0800 +Last-Translator: Christopher Meng +Language-Team: +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Plural-Forms: nplurals=1; plural=0; +X-Generator: Poedit 1.5.5 +TestPoString: abc123 +` diff --git a/vendor/github.com/chai2010/gettext-go/gettext/plural/doc.go b/vendor/github.com/chai2010/gettext-go/gettext/plural/doc.go new file mode 100644 index 0000000000..5641e2c3e7 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/plural/doc.go @@ -0,0 +1,36 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package plural provides standard plural formulas. + +Examples: + import ( + "code.google.com/p/gettext-go/gettext/plural" + ) + + func main() { + enFormula := plural.Formula("en_US") + xxFormula := plural.Formula("zh_CN") + + fmt.Printf("%s: %d\n", "en", enFormula(0)) + fmt.Printf("%s: %d\n", "en", enFormula(1)) + fmt.Printf("%s: %d\n", "en", enFormula(2)) + fmt.Printf("%s: %d\n", "??", xxFormula(0)) + fmt.Printf("%s: %d\n", "??", xxFormula(1)) + fmt.Printf("%s: %d\n", "??", xxFormula(2)) + fmt.Printf("%s: %d\n", "??", xxFormula(9)) + // Output: + // en: 0 + // en: 0 + // en: 1 + // ??: 0 + // ??: 0 + // ??: 1 + // ??: 8 + } + +See http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html +*/ +package plural diff --git a/vendor/github.com/chai2010/gettext-go/gettext/plural/formula.go b/vendor/github.com/chai2010/gettext-go/gettext/plural/formula.go new file mode 100644 index 0000000000..679a1cd50d --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/plural/formula.go @@ -0,0 +1,181 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plural + +import ( + "strings" +) + +// Formula provides the language's standard plural formula. +func Formula(lang string) func(n int) int { + if idx := index(lang); idx != -1 { + return formulaTable[fmtForms(FormsTable[idx].Value)] + } + if idx := index("??"); idx != -1 { + return formulaTable[fmtForms(FormsTable[idx].Value)] + } + return func(n int) int { + return n + } +} + +func index(lang string) int { + for i := 0; i < len(FormsTable); i++ { + if strings.HasPrefix(lang, FormsTable[i].Lang) { + return i + } + } + return -1 +} + +func fmtForms(forms string) string { + forms = strings.TrimSpace(forms) + forms = strings.Replace(forms, " ", "", -1) + return forms +} + +var formulaTable = map[string]func(n int) int{ + fmtForms("nplurals=n; plural=n-1;"): func(n int) int { + if n > 0 { + return n - 1 + } + return 0 + }, + fmtForms("nplurals=1; plural=0;"): func(n int) int { + return 0 + }, + fmtForms("nplurals=2; plural=(n != 1);"): func(n int) int { + if n <= 1 { + return 0 + } + return 1 + }, + fmtForms("nplurals=2; plural=(n > 1);"): func(n int) int { + if n <= 1 { + return 0 + } + return 1 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n != 0 { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;"): func(n int) int { + if n == 1 { + return 0 + } + if n == 2 { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;"): func(n int) int { + if n == 1 { + return 0 + } + if n == 0 || (n%100 > 0 && n%100 < 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n%10 >= 2 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n%10 == 1 && n%100 != 11 { + return 0 + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"): func(n int) int { + if n == 1 { + return 0 + } + if n >= 2 && n <= 4 { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"): func(n int) int { + if n == 1 { + return 0 + } + if n >= 2 && n <= 4 { + return 1 + } + return 2 + }, + fmtForms("nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"): func(n int) int { + if n == 1 { + return 0 + } + if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) { + return 1 + } + return 2 + }, + fmtForms("nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"): func(n int) int { + if n%100 == 1 { + return 0 + } + if n%100 == 2 { + return 1 + } + if n%100 == 3 || n%100 == 4 { + return 2 + } + return 3 + }, +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/plural/formula_test.go b/vendor/github.com/chai2010/gettext-go/gettext/plural/formula_test.go new file mode 100644 index 0000000000..ec756cae1b --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/plural/formula_test.go @@ -0,0 +1,50 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plural + +import ( + "testing" +) + +func TestFormula(t *testing.T) { + for i, v := range testData { + if out := Formula(v.lang)(v.in); out != v.out { + t.Fatalf("%d/%s: expect = %d, got = %d", i, v.lang, v.out, out) + } + } +} + +var testData = []struct { + lang string + in int + out int +}{ + {"#@", 0, 0}, + {"#@", 1, 0}, + {"#@", 10, 0}, + {"#@", -1, 0}, + + {"zh", 0, 0}, + {"zh", 1, 0}, + {"zh", 10, 0}, + {"zh", -1, 0}, + + {"zh_CN", 0, 0}, + {"zh_CN", 1, 0}, + {"zh_CN", 10, 0}, + {"zh_CN", -1, 0}, + + {"en", 0, 0}, + {"en", 1, 0}, + {"en", 2, 1}, + {"en", 10, 1}, + {"en", -1, 0}, + + {"en_US", 0, 0}, + {"en_US", 1, 0}, + {"en_US", 2, 1}, + {"en_US", 10, 1}, + {"en_US", -1, 0}, +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/plural/table.go b/vendor/github.com/chai2010/gettext-go/gettext/plural/table.go new file mode 100644 index 0000000000..cdc50d2110 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/plural/table.go @@ -0,0 +1,55 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plural + +// FormsTable are standard hard-coded plural rules. +// The application developers and the translators need to understand them. +// +// See GNU's gettext library source code: gettext/gettext-tools/src/plural-table.c +var FormsTable = []struct { + Lang string + Language string + Value string +}{ + {"??", "Unknown", "nplurals=1; plural=0;"}, + {"ja", "Japanese", "nplurals=1; plural=0;"}, + {"vi", "Vietnamese", "nplurals=1; plural=0;"}, + {"ko", "Korean", "nplurals=1; plural=0;"}, + {"en", "English", "nplurals=2; plural=(n != 1);"}, + {"de", "German", "nplurals=2; plural=(n != 1);"}, + {"nl", "Dutch", "nplurals=2; plural=(n != 1);"}, + {"sv", "Swedish", "nplurals=2; plural=(n != 1);"}, + {"da", "Danish", "nplurals=2; plural=(n != 1);"}, + {"no", "Norwegian", "nplurals=2; plural=(n != 1);"}, + {"nb", "Norwegian Bokmal", "nplurals=2; plural=(n != 1);"}, + {"nn", "Norwegian Nynorsk", "nplurals=2; plural=(n != 1);"}, + {"fo", "Faroese", "nplurals=2; plural=(n != 1);"}, + {"es", "Spanish", "nplurals=2; plural=(n != 1);"}, + {"pt", "Portuguese", "nplurals=2; plural=(n != 1);"}, + {"it", "Italian", "nplurals=2; plural=(n != 1);"}, + {"bg", "Bulgarian", "nplurals=2; plural=(n != 1);"}, + {"el", "Greek", "nplurals=2; plural=(n != 1);"}, + {"fi", "Finnish", "nplurals=2; plural=(n != 1);"}, + {"et", "Estonian", "nplurals=2; plural=(n != 1);"}, + {"he", "Hebrew", "nplurals=2; plural=(n != 1);"}, + {"eo", "Esperanto", "nplurals=2; plural=(n != 1);"}, + {"hu", "Hungarian", "nplurals=2; plural=(n != 1);"}, + {"tr", "Turkish", "nplurals=2; plural=(n != 1);"}, + {"pt_BR", "Brazilian", "nplurals=2; plural=(n > 1);"}, + {"fr", "French", "nplurals=2; plural=(n > 1);"}, + {"lv", "Latvian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"}, + {"ga", "Irish", "nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;"}, + {"ro", "Romanian", "nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;"}, + {"lt", "Lithuanian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"ru", "Russian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"uk", "Ukrainian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"be", "Belarusian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"sr", "Serbian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"hr", "Croatian", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"cs", "Czech", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"}, + {"sk", "Slovak", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"}, + {"pl", "Polish", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"}, + {"sl", "Slovenian", "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"}, +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/comment.go b/vendor/github.com/chai2010/gettext-go/gettext/po/comment.go new file mode 100644 index 0000000000..d4abe7c106 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/comment.go @@ -0,0 +1,270 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" +) + +// Comment represents every message's comments. +type Comment struct { + StartLine int // comment start line + TranslatorComment string // # translator-comments // TrimSpace + ExtractedComment string // #. extracted-comments + ReferenceFile []string // #: src/msgcmp.c:338 src/po-lex.c:699 + ReferenceLine []int // #: src/msgcmp.c:338 src/po-lex.c:699 + Flags []string // #, fuzzy,c-format,range:0..10 + PrevMsgContext string // #| msgctxt previous-context + PrevMsgId string // #| msgid previous-untranslated-string +} + +func (p *Comment) less(q *Comment) bool { + if p.StartLine != 0 || q.StartLine != 0 { + return p.StartLine < q.StartLine + } + if a, b := len(p.ReferenceFile), len(q.ReferenceFile); a != b { + return a < b + } + for i := 0; i < len(p.ReferenceFile); i++ { + if a, b := p.ReferenceFile[i], q.ReferenceFile[i]; a != b { + return a < b + } + if a, b := p.ReferenceLine[i], q.ReferenceLine[i]; a != b { + return a < b + } + } + return false +} + +func (p *Comment) readPoComment(r *lineReader) (err error) { + *p = Comment{} + if err = r.skipBlankLine(); err != nil { + return err + } + defer func(oldPos int) { + newPos := r.currentPos() + if newPos != oldPos && err == io.EOF { + err = nil + } + }(r.currentPos()) + + p.StartLine = r.currentPos() + 1 + for { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if len(s) == 0 || s[0] != '#' { + return + } + + if err = p.readTranslatorComment(r); err != nil { + return + } + if err = p.readExtractedComment(r); err != nil { + return + } + if err = p.readReferenceComment(r); err != nil { + return + } + if err = p.readFlagsComment(r); err != nil { + return + } + if err = p.readPrevMsgContext(r); err != nil { + return + } + if err = p.readPrevMsgId(r); err != nil { + return + } + } +} + +func (p *Comment) readTranslatorComment(r *lineReader) (err error) { + const prefix = "# " // .,:| + for { + var s string + if s, _, err = r.readLine(); err != nil { + return err + } + if len(s) < 1 || s[0] != '#' { + r.unreadLine() + return nil + } + if len(s) >= 2 { + switch s[1] { + case '.', ',', ':', '|': + r.unreadLine() + return nil + } + } + if p.TranslatorComment != "" { + p.TranslatorComment += "\n" + } + p.TranslatorComment += strings.TrimSpace(s[1:]) + } +} + +func (p *Comment) readExtractedComment(r *lineReader) (err error) { + const prefix = "#." + for { + var s string + if s, _, err = r.readLine(); err != nil { + return err + } + if len(s) < len(prefix) || s[:len(prefix)] != prefix { + r.unreadLine() + return nil + } + if p.ExtractedComment != "" { + p.ExtractedComment += "\n" + } + p.ExtractedComment += strings.TrimSpace(s[len(prefix):]) + } +} + +func (p *Comment) readReferenceComment(r *lineReader) (err error) { + const prefix = "#:" + for { + var s string + if s, _, err = r.readLine(); err != nil { + return err + } + if len(s) < len(prefix) || s[:len(prefix)] != prefix { + r.unreadLine() + return nil + } + ss := strings.Split(strings.TrimSpace(s[len(prefix):]), " ") + for i := 0; i < len(ss); i++ { + idx := strings.Index(ss[i], ":") + if idx <= 0 { + continue + } + name := strings.TrimSpace(ss[i][:idx]) + line, _ := strconv.Atoi(strings.TrimSpace(ss[i][idx+1:])) + p.ReferenceFile = append(p.ReferenceFile, name) + p.ReferenceLine = append(p.ReferenceLine, line) + } + } +} + +func (p *Comment) readFlagsComment(r *lineReader) (err error) { + const prefix = "#," + for { + var s string + if s, _, err = r.readLine(); err != nil { + return err + } + if len(s) < len(prefix) || s[:len(prefix)] != prefix { + r.unreadLine() + return nil + } + ss := strings.Split(strings.TrimSpace(s[len(prefix):]), ",") + for i := 0; i < len(ss); i++ { + p.Flags = append(p.Flags, strings.TrimSpace(ss[i])) + } + } +} + +func (p *Comment) readPrevMsgContext(r *lineReader) (err error) { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if !rePrevMsgContextComments.MatchString(s) { + return + } + p.PrevMsgContext, err = p.readString(r) + return +} + +func (p *Comment) readPrevMsgId(r *lineReader) (err error) { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if !rePrevMsgIdComments.MatchString(s) { + return + } + p.PrevMsgId, err = p.readString(r) + return +} + +func (p *Comment) readString(r *lineReader) (msg string, err error) { + var s string + if s, _, err = r.readLine(); err != nil { + return + } + msg += decodePoString(s) + for { + if s, _, err = r.readLine(); err != nil { + return + } + if !reStringLineComments.MatchString(s) { + r.unreadLine() + break + } + msg += decodePoString(s) + } + return +} + +// GetFuzzy gets the fuzzy flag. +func (p *Comment) GetFuzzy() bool { + for _, s := range p.Flags { + if s == "fuzzy" { + return true + } + } + return false +} + +// SetFuzzy sets the fuzzy flag. +func (p *Comment) SetFuzzy(fuzzy bool) { + // +} + +// String returns the po format comment string. +func (p Comment) String() string { + var buf bytes.Buffer + if p.TranslatorComment != "" { + ss := strings.Split(p.TranslatorComment, "\n") + for i := 0; i < len(ss); i++ { + fmt.Fprintf(&buf, "# %s\n", ss[i]) + } + } + if p.ExtractedComment != "" { + ss := strings.Split(p.ExtractedComment, "\n") + for i := 0; i < len(ss); i++ { + fmt.Fprintf(&buf, "#. %s\n", ss[i]) + } + } + if a, b := len(p.ReferenceFile), len(p.ReferenceLine); a != 0 && a == b { + fmt.Fprintf(&buf, "#:") + for i := 0; i < len(p.ReferenceFile); i++ { + fmt.Fprintf(&buf, " %s:%d", p.ReferenceFile[i], p.ReferenceLine[i]) + } + fmt.Fprintf(&buf, "\n") + } + if len(p.Flags) != 0 { + fmt.Fprintf(&buf, "#, %s", p.Flags[0]) + for i := 1; i < len(p.Flags); i++ { + fmt.Fprintf(&buf, ", %s", p.Flags[i]) + } + fmt.Fprintf(&buf, "\n") + } + if p.PrevMsgContext != "" { + s := encodeCommentPoString(p.PrevMsgContext) + fmt.Fprintf(&buf, "#| msgctxt %s\n", s) + } + if p.PrevMsgId != "" { + s := encodeCommentPoString(p.PrevMsgId) + fmt.Fprintf(&buf, "#| msgid %s\n", s) + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/comment_test.go b/vendor/github.com/chai2010/gettext-go/gettext/po/comment_test.go new file mode 100644 index 0000000000..224b2a6835 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/comment_test.go @@ -0,0 +1,207 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "reflect" + "testing" +) + +func TestPoComment(t *testing.T) { + var x Comment + for i := 0; i < len(testPoComments); i++ { + if i != 2 { + continue + } + err := x.readPoComment(newLineReader(testPoComments[i].Data)) + if err != nil { + t.Fatalf("%d: %v", i, err) + } + x.StartLine = 0 // ingore comment line + if !reflect.DeepEqual(&x, &testPoComments[i].PoComment) { + t.Logf("expect(%d):\n", i) + t.Logf("\n%v\n", &testPoComments[i].PoComment) + t.Logf("got(%d):\n", i) + t.Logf("\n%v\n", &x) + t.FailNow() + } + if testPoComments[i].CheckStringer { + s := testPoComments[i].PoComment.String() + if s != testPoComments[i].Data { + t.Logf("expect(%d):\n", i) + t.Logf("\n%s\n", testPoComments[i].Data) + t.Logf("got(%d):\n", i) + t.Logf("\n%s\n", testPoComments[i].PoComment.String()) + t.FailNow() + } + } + } +} + +type testPoComment struct { + CheckStringer bool + Data string + PoComment Comment +} + +var testPoComments = []testPoComment{ + + // -------------------------------------------------------------- + // CheckStringer: true + // -------------------------------------------------------------- + + testPoComment{ + CheckStringer: true, + Data: `# translator comments +`, + PoComment: Comment{ + TranslatorComment: `translator comments`, + }, + }, + testPoComment{ + CheckStringer: true, + Data: `# translator comments +`, + PoComment: Comment{ + TranslatorComment: `translator comments`, + }, + }, + + testPoComment{ + CheckStringer: true, + Data: `# translator-comments +# bad comment +#. extracted-comments +#: src/msgcmp.c:338 src/po-lex.c:699 src/msg.c:123 +#, fuzzy, c-format, range:0..10 +#| msgctxt "" +#| "previous-context1\n" +#| "previous-context2" +#| msgid "" +#| "previous-untranslated-string1\n" +#| "previous-untranslated-string2" +`, + PoComment: Comment{ + TranslatorComment: "translator-comments\nbad comment", + ExtractedComment: "extracted-comments", + ReferenceFile: []string{"src/msgcmp.c", "src/po-lex.c", "src/msg.c"}, + ReferenceLine: []int{338, 699, 123}, + Flags: []string{"fuzzy", "c-format", "range:0..10"}, + PrevMsgContext: "previous-context1\nprevious-context2", + PrevMsgId: "previous-untranslated-string1\nprevious-untranslated-string2", + }, + }, + + // -------------------------------------------------------------- + // CheckStringer: false + // -------------------------------------------------------------- + + testPoComment{ + CheckStringer: false, + Data: ` +# translator-comments +#bad comment +#. extracted-comments +#: src/msgcmp.c:338 src/po-lex.c:699 +#: src/msg.c:123 +#, fuzzy,c-format,range:0..10 +#| msgctxt "" +#| "previous-context1\n" +#| "previous-context2" +#| msgid "" +#| "previous-untranslated-string1\n" +#| "previous-untranslated-string2" +`, + PoComment: Comment{ + TranslatorComment: "translator-comments\nbad comment", + ExtractedComment: "extracted-comments", + ReferenceFile: []string{"src/msgcmp.c", "src/po-lex.c", "src/msg.c"}, + ReferenceLine: []int{338, 699, 123}, + Flags: []string{"fuzzy", "c-format", "range:0..10"}, + PrevMsgContext: "previous-context1\nprevious-context2", + PrevMsgId: "previous-untranslated-string1\nprevious-untranslated-string2", + }, + }, + testPoComment{ + CheckStringer: false, + Data: ` +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Poedit 1.5\n" +"Report-Msgid-Bugs-To: poedit@googlegroups.com\n" +"POT-Creation-Date: 2012-07-30 10:34+0200\n" +"PO-Revision-Date: 2013-12-25 09:32+0800\n" +"Last-Translator: chai2010 \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.5.7\n" +`, + PoComment: Comment{ + TranslatorComment: `SOME DESCRIPTIVE TITLE. +Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +This file is distributed under the same license as the PACKAGE package. +FIRST AUTHOR , YEAR. +`, + }, + }, + testPoComment{ + CheckStringer: false, + Data: ` +#. TRANSLATORS: This is version information in about dialog, it is followed +#. by version number when used (wxWidgets 2.8) +#: ../src/edframe.cpp:2431 +#| msgctxt "previous-context asdasd" +"asdad \n asdsad" +msgstr "" +`, + PoComment: Comment{ + ExtractedComment: `TRANSLATORS: This is version information in about dialog, it is followed +by version number when used (wxWidgets 2.8)`, + ReferenceFile: []string{"../src/edframe.cpp"}, + ReferenceLine: []int{2431}, + PrevMsgContext: "previous-context asdasd", + }, + }, + testPoComment{ + CheckStringer: false, + Data: ` +#: tst-gettext2.c:33 +msgid "First string for testing." +msgstr "Lang1: 1st string" +`, + PoComment: Comment{ + ReferenceFile: []string{"tst-gettext2.c"}, + ReferenceLine: []int{33}, + }, + }, + testPoComment{ + CheckStringer: false, + Data: ` +#: app/app_procs.c:307 +#, fuzzy, c-format +msgid "Can't find output format %s\n" +msgstr "" +"敲矾弊牢 '%s'甫 佬阑荐 绝嚼聪促\n" +"%s" +`, + PoComment: Comment{ + ReferenceFile: []string{"app/app_procs.c"}, + ReferenceLine: []int{307}, + Flags: []string{"fuzzy", "c-format"}, + }, + }, + + // -------------------------------------------------------------- + // END + // -------------------------------------------------------------- +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/doc.go b/vendor/github.com/chai2010/gettext-go/gettext/po/doc.go new file mode 100644 index 0000000000..12bac8f2a2 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/doc.go @@ -0,0 +1,24 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package po provides support for reading and writing GNU PO file. + +Examples: + import ( + "github.com/chai2010/gettext-go/gettext/po" + ) + + func main() { + poFile, err := po.Load("test.po") + if err != nil { + log.Fatal(err) + } + fmt.Printf("%v", poFile) + } + +The GNU PO file specification is at +http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html. +*/ +package po diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/file.go b/vendor/github.com/chai2010/gettext-go/gettext/po/file.go new file mode 100644 index 0000000000..a9b7abf949 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/file.go @@ -0,0 +1,75 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "sort" +) + +// File represents an PO File. +// +// See http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html +type File struct { + MimeHeader Header + Messages []Message +} + +// Load loads a named po file. +func Load(name string) (*File, error) { + data, err := ioutil.ReadFile(name) + if err != nil { + return nil, err + } + return LoadData(data) +} + +// LoadData loads po file format data. +func LoadData(data []byte) (*File, error) { + r := newLineReader(string(data)) + var file File + for { + var msg Message + if err := msg.readPoEntry(r); err != nil { + if err == io.EOF { + return &file, nil + } + return nil, err + } + if msg.MsgId == "" { + file.MimeHeader.parseHeader(&msg) + continue + } + file.Messages = append(file.Messages, msg) + } +} + +// Save saves a po file. +func (f *File) Save(name string) error { + return ioutil.WriteFile(name, []byte(f.String()), 0666) +} + +// Save returns a po file format data. +func (f *File) Data() []byte { + // sort the massge as ReferenceFile/ReferenceLine field + var messages []Message + messages = append(messages, f.Messages...) + sort.Sort(byMessages(messages)) + + var buf bytes.Buffer + fmt.Fprintf(&buf, "%s\n", f.MimeHeader.String()) + for i := 0; i < len(messages); i++ { + fmt.Fprintf(&buf, "%s\n", messages[i].String()) + } + return buf.Bytes() +} + +// String returns the po format file string. +func (f *File) String() string { + return string(f.Data()) +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/file_test.go b/vendor/github.com/chai2010/gettext-go/gettext/po/file_test.go new file mode 100644 index 0000000000..d0a8eccc00 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/file_test.go @@ -0,0 +1,13 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "testing" +) + +func TestPoFile(t *testing.T) { + // +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/header.go b/vendor/github.com/chai2010/gettext-go/gettext/po/header.go new file mode 100644 index 0000000000..a9b5b6671b --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/header.go @@ -0,0 +1,106 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "bytes" + "fmt" + "strings" +) + +// Header is the initial comments "SOME DESCRIPTIVE TITLE", "YEAR" +// and "FIRST AUTHOR , YEAR" ought to be replaced by sensible information. +// +// See http://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html#Header-Entry +type Header struct { + Comment // Header Comments + ProjectIdVersion string // Project-Id-Version: PACKAGE VERSION + ReportMsgidBugsTo string // Report-Msgid-Bugs-To: FIRST AUTHOR + POTCreationDate string // POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE + PORevisionDate string // PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE + LastTranslator string // Last-Translator: FIRST AUTHOR + LanguageTeam string // Language-Team: golang-china + Language string // Language: zh_CN + MimeVersion string // MIME-Version: 1.0 + ContentType string // Content-Type: text/plain; charset=UTF-8 + ContentTransferEncoding string // Content-Transfer-Encoding: 8bit + PluralForms string // Plural-Forms: nplurals=2; plural=n == 1 ? 0 : 1; + XGenerator string // X-Generator: Poedit 1.5.5 + UnknowFields map[string]string +} + +func (p *Header) parseHeader(msg *Message) { + if msg.MsgId != "" || msg.MsgStr == "" { + return + } + lines := strings.Split(msg.MsgStr, "\n") + for i := 0; i < len(lines); i++ { + idx := strings.Index(lines[i], ":") + if idx < 0 { + continue + } + key := strings.TrimSpace(lines[i][:idx]) + val := strings.TrimSpace(lines[i][idx+1:]) + switch strings.ToUpper(key) { + case strings.ToUpper("Project-Id-Version"): + p.ProjectIdVersion = val + case strings.ToUpper("Report-Msgid-Bugs-To"): + p.ReportMsgidBugsTo = val + case strings.ToUpper("POT-Creation-Date"): + p.POTCreationDate = val + case strings.ToUpper("PO-Revision-Date"): + p.PORevisionDate = val + case strings.ToUpper("Last-Translator"): + p.LastTranslator = val + case strings.ToUpper("Language-Team"): + p.LanguageTeam = val + case strings.ToUpper("Language"): + p.Language = val + case strings.ToUpper("MIME-Version"): + p.MimeVersion = val + case strings.ToUpper("Content-Type"): + p.ContentType = val + case strings.ToUpper("Content-Transfer-Encoding"): + p.ContentTransferEncoding = val + case strings.ToUpper("Plural-Forms"): + p.PluralForms = val + case strings.ToUpper("X-Generator"): + p.XGenerator = val + default: + if p.UnknowFields == nil { + p.UnknowFields = make(map[string]string) + } + p.UnknowFields[key] = val + } + } + p.Comment = msg.Comment +} + +// String returns the po format header string. +func (p Header) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "%s", p.Comment.String()) + fmt.Fprintf(&buf, `msgid ""`+"\n") + fmt.Fprintf(&buf, `msgstr ""`+"\n") + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Project-Id-Version", p.ProjectIdVersion) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Report-Msgid-Bugs-To", p.ReportMsgidBugsTo) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "POT-Creation-Date", p.POTCreationDate) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "PO-Revision-Date", p.PORevisionDate) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Last-Translator", p.LastTranslator) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Language-Team", p.LanguageTeam) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Language", p.Language) + if p.MimeVersion != "" { + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "MIME-Version", p.MimeVersion) + } + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Content-Type", p.ContentType) + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "Content-Transfer-Encoding", p.ContentTransferEncoding) + if p.XGenerator != "" { + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", "X-Generator", p.XGenerator) + } + for k, v := range p.UnknowFields { + fmt.Fprintf(&buf, `"%s: %s\n"`+"\n", k, v) + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/header_test.go b/vendor/github.com/chai2010/gettext-go/gettext/po/header_test.go new file mode 100644 index 0000000000..a1d7bb54b2 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/header_test.go @@ -0,0 +1,13 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "testing" +) + +func TestHeader(t *testing.T) { + // +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/line_reader.go b/vendor/github.com/chai2010/gettext-go/gettext/po/line_reader.go new file mode 100644 index 0000000000..8597273a2b --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/line_reader.go @@ -0,0 +1,62 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "io" + "strings" +) + +type lineReader struct { + lines []string + pos int +} + +func newLineReader(data string) *lineReader { + data = strings.Replace(data, "\r", "", -1) + lines := strings.Split(data, "\n") + return &lineReader{lines: lines} +} + +func (r *lineReader) skipBlankLine() error { + for ; r.pos < len(r.lines); r.pos++ { + if strings.TrimSpace(r.lines[r.pos]) != "" { + break + } + } + if r.pos >= len(r.lines) { + return io.EOF + } + return nil +} + +func (r *lineReader) currentPos() int { + return r.pos +} + +func (r *lineReader) currentLine() (s string, pos int, err error) { + if r.pos >= len(r.lines) { + err = io.EOF + return + } + s, pos = r.lines[r.pos], r.pos + return +} + +func (r *lineReader) readLine() (s string, pos int, err error) { + if r.pos >= len(r.lines) { + err = io.EOF + return + } + s, pos = r.lines[r.pos], r.pos + r.pos++ + return +} + +func (r *lineReader) unreadLine() { + if r.pos >= 0 { + r.pos-- + } +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/message.go b/vendor/github.com/chai2010/gettext-go/gettext/po/message.go new file mode 100644 index 0000000000..a2cf2512c7 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/message.go @@ -0,0 +1,189 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" +) + +// A PO file is made up of many entries, +// each entry holding the relation between an original untranslated string +// and its corresponding translation. +// +// See http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html +type Message struct { + Comment // Coments + MsgContext string // msgctxt context + MsgId string // msgid untranslated-string + MsgIdPlural string // msgid_plural untranslated-string-plural + MsgStr string // msgstr translated-string + MsgStrPlural []string // msgstr[0] translated-string-case-0 +} + +type byMessages []Message + +func (d byMessages) Len() int { + return len(d) +} +func (d byMessages) Less(i, j int) bool { + if d[i].Comment.less(&d[j].Comment) { + return true + } + if a, b := d[i].MsgContext, d[j].MsgContext; a != b { + return a < b + } + if a, b := d[i].MsgId, d[j].MsgId; a != b { + return a < b + } + if a, b := d[i].MsgIdPlural, d[j].MsgIdPlural; a != b { + return a < b + } + return false +} +func (d byMessages) Swap(i, j int) { + d[i], d[j] = d[j], d[i] +} + +func (p *Message) readPoEntry(r *lineReader) (err error) { + *p = Message{} + if err = r.skipBlankLine(); err != nil { + return + } + defer func(oldPos int) { + newPos := r.currentPos() + if newPos != oldPos && err == io.EOF { + err = nil + } + }(r.currentPos()) + + if err = p.Comment.readPoComment(r); err != nil { + return + } + for { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + + if p.isInvalidLine(s) { + err = fmt.Errorf("gettext: line %d, %v", r.currentPos(), "invalid line") + return + } + if reComment.MatchString(s) || reBlankLine.MatchString(s) { + return + } + + if err = p.readMsgContext(r); err != nil { + return + } + if err = p.readMsgId(r); err != nil { + return + } + if err = p.readMsgIdPlural(r); err != nil { + return + } + if err = p.readMsgStrOrPlural(r); err != nil { + return + } + } +} + +func (p *Message) readMsgContext(r *lineReader) (err error) { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if !reMsgContext.MatchString(s) { + return + } + p.MsgContext, err = p.readString(r) + return +} + +func (p *Message) readMsgId(r *lineReader) (err error) { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if !reMsgId.MatchString(s) { + return + } + p.MsgId, err = p.readString(r) + return +} + +func (p *Message) readMsgIdPlural(r *lineReader) (err error) { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if !reMsgIdPlural.MatchString(s) { + return + } + p.MsgIdPlural, err = p.readString(r) + return nil +} + +func (p *Message) readMsgStrOrPlural(r *lineReader) (err error) { + var s string + if s, _, err = r.currentLine(); err != nil { + return + } + if !reMsgStr.MatchString(s) && !reMsgStrPlural.MatchString(s) { + return + } + if reMsgStrPlural.MatchString(s) { + left, right := strings.Index(s, `[`), strings.LastIndex(s, `]`) + idx, _ := strconv.Atoi(s[left+1 : right]) + s, err = p.readString(r) + if n := len(p.MsgStrPlural); (idx + 1) > n { + p.MsgStrPlural = append(p.MsgStrPlural, make([]string, (idx+1)-n)...) + } + p.MsgStrPlural[idx] = s + } else { + p.MsgStr, err = p.readString(r) + } + return nil +} + +func (p *Message) readString(r *lineReader) (msg string, err error) { + var s string + if s, _, err = r.readLine(); err != nil { + return + } + msg += decodePoString(s) + for { + if s, _, err = r.readLine(); err != nil { + return + } + if !reStringLine.MatchString(s) { + r.unreadLine() + break + } + msg += decodePoString(s) + } + return +} + +// String returns the po format entry string. +func (p Message) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "%s", p.Comment.String()) + fmt.Fprintf(&buf, "msgid %s", encodePoString(p.MsgId)) + if p.MsgIdPlural != "" { + fmt.Fprintf(&buf, "msgid_plural %s", encodePoString(p.MsgIdPlural)) + } + if p.MsgStr != "" { + fmt.Fprintf(&buf, "msgstr %s", encodePoString(p.MsgStr)) + } + for i := 0; i < len(p.MsgStrPlural); i++ { + fmt.Fprintf(&buf, "msgstr[%d] %s", i, encodePoString(p.MsgStrPlural[i])) + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/message_test.go b/vendor/github.com/chai2010/gettext-go/gettext/po/message_test.go new file mode 100644 index 0000000000..e58f59fedf --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/message_test.go @@ -0,0 +1,75 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "reflect" + "testing" +) + +func _TestPoEntry(t *testing.T) { + if len(testPoEntrys) != len(testPoEntryStrings) { + t.Fatalf("bad test") + } + var entry Message + for i := 0; i < len(testPoEntrys); i++ { + if err := entry.readPoEntry(newLineReader(testPoEntryStrings[i])); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(&entry, &testPoEntrys[i]) { + t.Fatalf("%d: expect = %v, got = %v", i, testPoEntrys[i], entry) + } + } +} + +var testPoEntryStrings = []string{ + ` +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 项目名称\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-12 20:03+0000\n" +"PO-Revision-Date: 2013-12-02 17:05+0800\n" +"Last-Translator: chai2010 \n" +"Language-Team: chai2010(团队) \n" +"Language: 中文\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-SourceCharset: UTF-8\n" +`, +} + +var testPoEntrys = []Message{ + Message{ + Comment: Comment{ + TranslatorComment: `SOME DESCRIPTIVE TITLE. +Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +This file is distributed under the same license as the PACKAGE package. +FIRST AUTHOR , YEAR. +`, + }, + MsgStr: ` +Project-Id-Version: 项目名称 +Report-Msgid-Bugs-To: +POT-Creation-Date: 2011-12-12 20:03+0000 +PO-Revision-Date: 2013-12-02 17:05+0800 +Last-Translator: chai2010 +Language-Team: chai2010(团队) +Language: 中文 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +X-Generator: Poedit 1.5.7 +X-Poedit-SourceCharset: UTF-8 +`, + }, +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/poedit_test.go b/vendor/github.com/chai2010/gettext-go/gettext/po/poedit_test.go new file mode 100644 index 0000000000..1b0128c4fc --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/poedit_test.go @@ -0,0 +1,35 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "reflect" + "testing" +) + +var ( + testPoEditPoFile = "../testdata/poedit-1.5.7-zh_CN.po" + testPoEditMoFile = "../testdata/poedit-1.5.7-zh_CN.mo" +) + +func _TestPoEditPoFile(t *testing.T) { + po, err := Load(testPoEditPoFile) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(&po.MimeHeader, &poEditFile.MimeHeader) { + t.Fatalf("expect = %v, got = %v", &poEditFile.MimeHeader, &po.MimeHeader) + } + if len(po.Messages) != len(poEditFile.Messages) { + t.Fatal("size not equal") + } + for k, v0 := range po.Messages { + if v1 := poEditFile.Messages[k]; !reflect.DeepEqual(&v0, &v1) { + t.Fatalf("%d: expect = %v, got = %v", k, v1, v0) + } + } +} + +var poEditFile = &File{} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/re.go b/vendor/github.com/chai2010/gettext-go/gettext/po/re.go new file mode 100644 index 0000000000..67c240a57b --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/re.go @@ -0,0 +1,58 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "regexp" +) + +var ( + reComment = regexp.MustCompile(`^#`) // # + reExtractedComments = regexp.MustCompile(`^#\.`) // #. + reReferenceComments = regexp.MustCompile(`^#:`) // #: + reFlagsComments = regexp.MustCompile(`^#,`) // #, fuzzy,c-format + rePrevMsgContextComments = regexp.MustCompile(`^#\|\s+msgctxt`) // #| msgctxt + rePrevMsgIdComments = regexp.MustCompile(`^#\|\s+msgid`) // #| msgid + reStringLineComments = regexp.MustCompile(`^#\|\s+".*"\s*$`) // #| "message" + + reMsgContext = regexp.MustCompile(`^msgctxt\s+".*"\s*$`) // msgctxt + reMsgId = regexp.MustCompile(`^msgid\s+".*"\s*$`) // msgid + reMsgIdPlural = regexp.MustCompile(`^msgid_plural\s+".*"\s*$`) // msgid_plural + reMsgStr = regexp.MustCompile(`^msgstr\s*".*"\s*$`) // msgstr + reMsgStrPlural = regexp.MustCompile(`^msgstr\s*(\[\d+\])\s*".*"\s*$`) // msgstr[0] + reStringLine = regexp.MustCompile(`^\s*".*"\s*$`) // "message" + reBlankLine = regexp.MustCompile(`^\s*$`) // +) + +func (p *Message) isInvalidLine(s string) bool { + if reComment.MatchString(s) { + return false + } + if reBlankLine.MatchString(s) { + return false + } + + if reMsgContext.MatchString(s) { + return false + } + if reMsgId.MatchString(s) { + return false + } + if reMsgIdPlural.MatchString(s) { + return false + } + if reMsgStr.MatchString(s) { + return false + } + if reMsgStrPlural.MatchString(s) { + return false + } + + if reStringLine.MatchString(s) { + return false + } + + return true +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/util.go b/vendor/github.com/chai2010/gettext-go/gettext/po/util.go new file mode 100644 index 0000000000..52544832cf --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/util.go @@ -0,0 +1,110 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "bytes" + "strings" +) + +func decodePoString(text string) string { + lines := strings.Split(text, "\n") + for i := 0; i < len(lines); i++ { + left := strings.Index(lines[i], `"`) + right := strings.LastIndex(lines[i], `"`) + if left < 0 || right < 0 || left == right { + lines[i] = "" + continue + } + line := lines[i][left+1 : right] + data := make([]byte, 0, len(line)) + for i := 0; i < len(line); i++ { + if line[i] != '\\' { + data = append(data, line[i]) + continue + } + if i+1 >= len(line) { + break + } + switch line[i+1] { + case 'n': // \\n -> \n + data = append(data, '\n') + i++ + case 't': // \\t -> \n + data = append(data, '\t') + i++ + case '\\': // \\\ -> ? + data = append(data, '\\') + i++ + } + } + lines[i] = string(data) + } + return strings.Join(lines, "") +} + +func encodePoString(text string) string { + var buf bytes.Buffer + lines := strings.Split(text, "\n") + for i := 0; i < len(lines); i++ { + if lines[i] == "" { + if i != len(lines)-1 { + buf.WriteString(`"\n"` + "\n") + } + continue + } + buf.WriteRune('"') + for _, r := range lines[i] { + switch r { + case '\\': + buf.WriteString(`\\`) + case '"': + buf.WriteString(`\"`) + case '\n': + buf.WriteString(`\n`) + case '\t': + buf.WriteString(`\t`) + default: + buf.WriteRune(r) + } + } + buf.WriteString(`\n"` + "\n") + } + return buf.String() +} + +func encodeCommentPoString(text string) string { + var buf bytes.Buffer + lines := strings.Split(text, "\n") + if len(lines) > 1 { + buf.WriteString(`""` + "\n") + } + for i := 0; i < len(lines); i++ { + if len(lines) > 0 { + buf.WriteString("#| ") + } + buf.WriteRune('"') + for _, r := range lines[i] { + switch r { + case '\\': + buf.WriteString(`\\`) + case '"': + buf.WriteString(`\"`) + case '\n': + buf.WriteString(`\n`) + case '\t': + buf.WriteString(`\t`) + default: + buf.WriteRune(r) + } + } + if i < len(lines)-1 { + buf.WriteString(`\n"` + "\n") + } else { + buf.WriteString(`"`) + } + } + return buf.String() +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/po/util_test.go b/vendor/github.com/chai2010/gettext-go/gettext/po/util_test.go new file mode 100644 index 0000000000..5e8331e143 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/po/util_test.go @@ -0,0 +1,68 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package po + +import ( + "testing" +) + +func TestDecodePoString(t *testing.T) { + if s := decodePoString(poStrEncode); s != poStrDecode { + t.Fatalf(`expect = %s got = %s`, poStrDecode, s) + } +} + +func TestEncodePoString(t *testing.T) { + if s := encodePoString(poStrDecode); s != poStrEncodeStd { + t.Fatalf(`expect = %s; got = %s`, poStrEncodeStd, s) + } +} + +const poStrEncode = `# noise +123456789 +"Project-Id-Version: Poedit 1.5\n" +"Report-Msgid-Bugs-To: poedit@googlegroups.com\n" +"POT-Creation-Date: 2012-07-30 10:34+0200\n" +"PO-Revision-Date: 2013-02-24 21:00+0800\n" +"Last-Translator: Christopher Meng \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.5.5\n" +"TestPoString: abc" +"123\n" +>> +123456??? +` + +const poStrEncodeStd = `"Project-Id-Version: Poedit 1.5\n" +"Report-Msgid-Bugs-To: poedit@googlegroups.com\n" +"POT-Creation-Date: 2012-07-30 10:34+0200\n" +"PO-Revision-Date: 2013-02-24 21:00+0800\n" +"Last-Translator: Christopher Meng \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.5.5\n" +"TestPoString: abc123\n" +` + +const poStrDecode = `Project-Id-Version: Poedit 1.5 +Report-Msgid-Bugs-To: poedit@googlegroups.com +POT-Creation-Date: 2012-07-30 10:34+0200 +PO-Revision-Date: 2013-02-24 21:00+0800 +Last-Translator: Christopher Meng +Language-Team: +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Plural-Forms: nplurals=1; plural=0; +X-Generator: Poedit 1.5.5 +TestPoString: abc123 +` diff --git a/vendor/github.com/chai2010/gettext-go/gettext/testdata_test.go b/vendor/github.com/chai2010/gettext-go/gettext/testdata_test.go new file mode 100644 index 0000000000..61fa151ca6 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/testdata_test.go @@ -0,0 +1,62 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "reflect" + "testing" +) + +var testDataDir = "../testdata/" + +var testPoMoFiles = []struct { + poFile string + moFile string +}{ + {"gettext-3-1.po", "gettext-3-1.mo"}, + {"gettext-4.po", "gettext-4.mo"}, + {"gettext-5.po", "gettext-5.mo"}, + {"gettext-6-1.po", "gettext-6-1.mo"}, + {"gettext-6-2.po", "gettext-6-2.mo"}, + {"gettext-7.po", "gettext-7.mo"}, + {"gettextpo-1.de.po", "gettextpo-1.de.mo"}, + {"mm-ko-comp.euc-kr.po", "mm-ko-comp.euc-kr.mo"}, + {"mm-ko.euc-kr.po", "mm-ko.euc-kr.mo"}, + {"mm-viet.comp.po", "mm-viet.comp.mo"}, + {"poedit-1.5.7-zh_CN.po", "poedit-1.5.7-zh_CN.mo"}, + {"qttest2_de.po", "qttest2_de.mo"}, + {"qttest_pl.po", "qttest_pl.mo"}, + {"test.po", "test.mo"}, +} + +func TestPoMoFiles(t *testing.T) { + for i := 0; i < len(testPoMoFiles); i++ { + poName := testPoMoFiles[i].poFile + moName := testPoMoFiles[i].moFile + po, err := newPoTranslator(testDataDir+poName, nil) + if err != nil { + t.Fatalf("%s: %v", poName, err) + } + mo, err := newMoTranslator(testDataDir+moName, nil) + if err != nil { + t.Fatalf("%s: %v", poName, err) + } + // if no translate, the mo will drop the message. + // so len(mo) may less than len(po). + if a, b := len(po.MessageMap), len(mo.MessageMap); a != b { + t.Logf("%s: %v, %d != %d", poName, "size not equal", a, b) + } + for k, v0 := range po.MessageMap { + v1, ok := mo.MessageMap[k] + if !ok { + t.Logf("%s: %q: missing", poName, v0.MsgId) + continue + } + if !reflect.DeepEqual(&v0, &v1) { + t.Fatalf("%s: %q: expect = %v, got = %v", poName, v0.MsgId, v0, v1) + } + } + } +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/tr.go b/vendor/github.com/chai2010/gettext-go/gettext/tr.go new file mode 100644 index 0000000000..fedfbc301d --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/tr.go @@ -0,0 +1,128 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "github.com/chai2010/gettext-go/gettext/mo" + "github.com/chai2010/gettext-go/gettext/plural" + "github.com/chai2010/gettext-go/gettext/po" +) + +var nilTranslator = &translator{ + MessageMap: make(map[string]mo.Message), + PluralFormula: plural.Formula("??"), +} + +type translator struct { + MessageMap map[string]mo.Message + PluralFormula func(n int) int +} + +func newMoTranslator(name string, data []byte) (*translator, error) { + var ( + f *mo.File + err error + ) + if len(data) != 0 { + f, err = mo.LoadData(data) + } else { + f, err = mo.Load(name) + } + if err != nil { + return nil, err + } + var tr = &translator{ + MessageMap: make(map[string]mo.Message), + } + for _, v := range f.Messages { + tr.MessageMap[tr.makeMapKey(v.MsgContext, v.MsgId)] = v + } + if lang := f.MimeHeader.Language; lang != "" { + tr.PluralFormula = plural.Formula(lang) + } else { + tr.PluralFormula = plural.Formula("??") + } + return tr, nil +} + +func newPoTranslator(name string, data []byte) (*translator, error) { + var ( + f *po.File + err error + ) + if len(data) != 0 { + f, err = po.LoadData(data) + } else { + f, err = po.Load(name) + } + if err != nil { + return nil, err + } + var tr = &translator{ + MessageMap: make(map[string]mo.Message), + } + for _, v := range f.Messages { + tr.MessageMap[tr.makeMapKey(v.MsgContext, v.MsgId)] = mo.Message{ + MsgContext: v.MsgContext, + MsgId: v.MsgId, + MsgIdPlural: v.MsgIdPlural, + MsgStr: v.MsgStr, + MsgStrPlural: v.MsgStrPlural, + } + } + if lang := f.MimeHeader.Language; lang != "" { + tr.PluralFormula = plural.Formula(lang) + } else { + tr.PluralFormula = plural.Formula("??") + } + return tr, nil +} + +func (p *translator) PGettext(msgctxt, msgid string) string { + return p.PNGettext(msgctxt, msgid, "", 0) +} + +func (p *translator) PNGettext(msgctxt, msgid, msgidPlural string, n int) string { + n = p.PluralFormula(n) + if ss := p.findMsgStrPlural(msgctxt, msgid, msgidPlural); len(ss) != 0 { + if n >= len(ss) { + n = len(ss) - 1 + } + if ss[n] != "" { + return ss[n] + } + } + if msgidPlural != "" && n > 0 { + return msgidPlural + } + return msgid +} + +func (p *translator) findMsgStrPlural(msgctxt, msgid, msgidPlural string) []string { + key := p.makeMapKey(msgctxt, msgid) + if v, ok := p.MessageMap[key]; ok { + if len(v.MsgIdPlural) != 0 { + if len(v.MsgStrPlural) != 0 { + return v.MsgStrPlural + } else { + return nil + } + } else { + if len(v.MsgStr) != 0 { + return []string{v.MsgStr} + } else { + return nil + } + } + } + return nil +} + +func (p *translator) makeMapKey(msgctxt, msgid string) string { + if msgctxt != "" { + return msgctxt + mo.EotSeparator + msgid + } + return msgid +} diff --git a/vendor/github.com/chai2010/gettext-go/gettext/tr_test.go b/vendor/github.com/chai2010/gettext-go/gettext/tr_test.go new file mode 100644 index 0000000000..ac4a09e863 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/gettext/tr_test.go @@ -0,0 +1,100 @@ +// Copyright 2013 ChaiShushan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gettext + +import ( + "testing" + + "github.com/chai2010/gettext-go/gettext/mo" + "github.com/chai2010/gettext-go/gettext/po" +) + +func TestTranslator_Po(t *testing.T) { + tr, err := newPoTranslator("test", []byte(testTrPoData)) + if err != nil { + t.Fatal(err) + } + for _, v := range testTrData { + if out := tr.PGettext(v.msgctxt, v.msgid); out != v.msgstr { + t.Fatalf("%s/%s: expect = %s, got = %s", v.msgctxt, v.msgid, v.msgstr, out) + } + } +} + +func TestTranslator_Mo(t *testing.T) { + tr, err := newMoTranslator("test", poToMoData(t, []byte(testTrPoData))) + if err != nil { + t.Fatal(err) + } + for _, v := range testTrData { + if out := tr.PGettext(v.msgctxt, v.msgid); out != v.msgstr { + t.Fatalf("%s/%s: expect = %s, got = %s", v.msgctxt, v.msgid, v.msgstr, out) + } + break + } +} + +func poToMoData(t *testing.T, data []byte) []byte { + poFile, err := po.LoadData(data) + if err != nil { + t.Fatal(err) + } + moFile := &mo.File{ + MimeHeader: mo.Header{ + ProjectIdVersion: poFile.MimeHeader.ProjectIdVersion, + ReportMsgidBugsTo: poFile.MimeHeader.ReportMsgidBugsTo, + POTCreationDate: poFile.MimeHeader.POTCreationDate, + PORevisionDate: poFile.MimeHeader.PORevisionDate, + LastTranslator: poFile.MimeHeader.LastTranslator, + LanguageTeam: poFile.MimeHeader.LanguageTeam, + Language: poFile.MimeHeader.Language, + MimeVersion: poFile.MimeHeader.MimeVersion, + ContentType: poFile.MimeHeader.ContentType, + ContentTransferEncoding: poFile.MimeHeader.ContentTransferEncoding, + PluralForms: poFile.MimeHeader.PluralForms, + XGenerator: poFile.MimeHeader.XGenerator, + UnknowFields: poFile.MimeHeader.UnknowFields, + }, + } + for _, v := range poFile.Messages { + moFile.Messages = append(moFile.Messages, mo.Message{ + MsgContext: v.MsgContext, + MsgId: v.MsgId, + MsgIdPlural: v.MsgIdPlural, + MsgStr: v.MsgStr, + MsgStrPlural: v.MsgStrPlural, + }) + } + return moFile.Data() +} + +var testTrData = []struct { + msgctxt string + msgid string + msgstr string +}{ + {"main.init", "Gettext in init.", "Init函数中的Gettext."}, + {"main.main", "Hello, world!", "你好, 世界!"}, + {"main.func", "Gettext in func.", "闭包函数中的Gettext."}, + {"code.google.com/p/gettext-go/examples/hi.SayHi", "pkg hi: Hello, world!", "来自\"Hi\"包的问候: 你好, 世界!"}, +} + +var testTrPoData = ` +msgctxt "main.init" +msgid "Gettext in init." +msgstr "Init函数中的Gettext." + +msgctxt "main.main" +msgid "Hello, world!" +msgstr "你好, 世界!" + +msgctxt "main.func" +msgid "Gettext in func." +msgstr "闭包函数中的Gettext." + +msgctxt "code.google.com/p/gettext-go/examples/hi.SayHi" +msgid "pkg hi: Hello, world!" +msgstr "来自\"Hi\"包的问候: 你好, 世界!" +` diff --git a/vendor/github.com/chai2010/gettext-go/testdata/Makefile b/vendor/github.com/chai2010/gettext-go/testdata/Makefile new file mode 100644 index 0000000000..c809bd45d7 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/Makefile @@ -0,0 +1,14 @@ +# Copyright 2013 ChaiShushan . All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +PO_FILES = $(wildcard *.po) +MO_FILES = $(patsubst %.po,%.mo,$(PO_FILES)) + +default: $(MO_FILES) + +clean: + rm *.mo + +%.mo: %.po + msgfmt -o $@ $< diff --git a/vendor/github.com/chai2010/gettext-go/testdata/README.txt b/vendor/github.com/chai2010/gettext-go/testdata/README.txt new file mode 100644 index 0000000000..a5187bfe93 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/README.txt @@ -0,0 +1 @@ +xg-c-1.ok.po has a bad header, msgfmt can't compile it. diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-3-1.mo b/vendor/github.com/chai2010/gettext-go/testdata/gettext-3-1.mo new file mode 100644 index 0000000000000000000000000000000000000000..79f25af10c75d1647ece49566c60a7e77b735d86 GIT binary patch literal 282 zcmca7#4?ou2$+Fb28d07m=%b9fEWZ4fLI!c6M!xWUa94Jj@S^Z$?0Gsv^aA0u-BH_a<$Vw8178Rsypt* n@$aeE6gI}IKCRpu6C>GDOUpSi)U<6xvO(+j^v_BC`ycTD2E;_W literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-3-2.po b/vendor/github.com/chai2010/gettext-go/testdata/gettext-3-2.po new file mode 100644 index 0000000000..d1606c8ede --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/gettext-3-2.po @@ -0,0 +1,13 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: 7-bit\n" + +#: tst-gettext2.c:33 +msgid "First string for testing." +msgstr "Lang2: 1st string" + +#: tst-gettext2.c:34 +msgid "Another string for testing." +msgstr "Lang2: 2nd string" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-4.mo b/vendor/github.com/chai2010/gettext-go/testdata/gettext-4.mo new file mode 100644 index 0000000000000000000000000000000000000000..4d4b74fb3ae98e44de491c3d9eb2e1ddfa122249 GIT binary patch literal 190 zcmca7#4?ou2$+Ca28eZlm=%a^fEWYToW#sLYlY;D#G>NV v5?jw;e_aa;Q%hY#gyN#ayyCRfB3;+KOc#J4( literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-4.po b/vendor/github.com/chai2010/gettext-go/testdata/gettext-4.po new file mode 100644 index 0000000000..9a6231d600 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/gettext-4.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8-bit\n" + +msgid "cheese" +msgstr "Kse" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-5.mo b/vendor/github.com/chai2010/gettext-go/testdata/gettext-5.mo new file mode 100644 index 0000000000000000000000000000000000000000..4d4b74fb3ae98e44de491c3d9eb2e1ddfa122249 GIT binary patch literal 190 zcmca7#4?ou2$+Ca28eZlm=%a^fEWYToW#sLYlY;D#G>NV v5?jw;e_aa;Q%hY#gyN#ayyCRfB3;+KOc#J4( literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-5.po b/vendor/github.com/chai2010/gettext-go/testdata/gettext-5.po new file mode 100644 index 0000000000..9a6231d600 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/gettext-5.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8-bit\n" + +msgid "cheese" +msgstr "Kse" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-6-1.mo b/vendor/github.com/chai2010/gettext-go/testdata/gettext-6-1.mo new file mode 100644 index 0000000000000000000000000000000000000000..062a3a26a900145452e86b0b0ecba9d467bf5b2b GIT binary patch literal 195 zcmca7#4?ou2$+Ca28eZlm=%a^fEWYFcT+mReMtnV)B+V5n!n<(!{alA2ed8&X-2YNb$;T2Z23kdv61XRVN&kyuom wT4L)N?5}HKVQQ&sh)`UVm{**ZTBPfmmzToW#sLYlY;D#G>NV v5?jw;e_aa;Q%hY#gyN#ayyCRfB3;+KOc#J4( literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettext-7.po b/vendor/github.com/chai2010/gettext-go/testdata/gettext-7.po new file mode 100644 index 0000000000..9a6231d600 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/gettext-7.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8-bit\n" + +msgid "cheese" +msgstr "Kse" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.mo b/vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.mo new file mode 100644 index 0000000000000000000000000000000000000000..915168bb050b3d85d89d291f9180ebb56c8fba09 GIT binary patch literal 681 zcmZ9J&2AGh5XTLK11ljU&K!nAC92}Z8(IiXf})nDR7%vSEO6oC-ORcR8+)<6dq;EpdZ5Cw7Rh z#E(mi{UUAs`Z8nJNE^~^(sNRBGm>R)5_YBCwmkSItDVi!dp5E88}LB|?3~nSn1alP zP$Ad?-j@d1XK~jwiwxbDwa5Q;&76IWdEm!${sNsZt&vcbnZ^+CL#Qo?IO)Ynbc(fg zfls|I=X}4>o~Kqq)@ZIx!`I;>I>*-OXm*nF1BWV5KK_Inlh753;Nk%nyC4#|_i#sa zg`hxwitoy{?*DOnknGC)k5+LLCUU9&Y|8iA8i^ zO+Z6EeXQwm?O)xiD^;34Jc0XdzmLva!7vEvavm?CtSrpVOLz*#+E9LVugqLLfJMh*n IRzAc20PKp#KmY&$ literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.po b/vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.po new file mode 100644 index 0000000000..f7451a90d6 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/gettextpo-1.de.po @@ -0,0 +1,43 @@ +# Test case for the libgettextpo library. +msgid "" +msgstr "" +"Project-Id-Version: libgettextpo 0.18.1\n" +"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n" +"POT-Creation-Date: 2010-06-04 01:57+0200\n" +"PO-Revision-Date: 2010-06-05 14:39+0200\n" +"Last-Translator: Bruno Haible \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: gnulib-lib/w32spawn.h:81 +#, fuzzy, c-format +msgid "cannot restore fd %d: dup2 failed" +msgstr "Ausgabedatei »%s« kann nicht erstellt werden" + +#: gnulib-lib/wait-process.c:223 gnulib-lib/wait-process.c:255 +#: gnulib-lib/wait-process.c:317 +#, c-format +msgid "%s subprocess" +msgstr "Subprozeß %s" + +# Adjektiv, kein ganzer Satz! +#. Denote a lock's state +msgctxt "Lock state" +msgid "Open" +msgstr "Geöffnet" + +# Französische Weine sind die besten der Welt. +#, java-format +msgid "a bottle of wine" +msgid_plural "{0,number} bottles of wine" +msgstr[0] "eine Flasche Wein" +msgstr[1] "{0,number} Weinflaschen" + +#. Denote a lock's state +#~ msgctxt "Lock state" +#~ msgid "Closed" +#~ msgstr "Geschlossen" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.mo b/vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.mo new file mode 100644 index 0000000000000000000000000000000000000000..911d355e984eb89a63c8f19089c078d0f9fafafe GIT binary patch literal 5881 zcmb`J3yf4(8OM*cmAbx=R>i9Au?3kGW?(^H!=o&_v#@1f*$1W=Gv1v!J3D3e-f`v* zk3@~e*rYKeHAZ97YQm!>s4TOvTV`LgcUYF$!UB=BSeCK~)<8u_6x%Op*;hneh>H&a54BOSPXJ-Blr}!6?_DI3H&5@0Hpaj5I=Sje;)_m1fK@q z0kIXE1!YM7T*YM|sT=?ofzPP^?|?JW{vr4=a1TiP90WfB9tB}CdjtG1_*)P^b_stZ z|BoQ8zoj@GlYWMFAxQI&;E(JbQoIa)5$)R`2Ole7Y!kQw{51Ge@G)>d_yjlz()z24 z{{&~C{V1HU7MugF2A>1hgZsd8@GoEmxDrY(1z!Nk?i@(*`5X8J&;z46SO$_GBOp{` zTR@n^z6*N67r-xphrvQ{1pF-cFcy*gC&4GdO(3oNHuxFvc@SH%Uw}}F{R*VG9R|tH zJ`keVu+g~zNq>$AjRz{Nb-k4 zlJ}0{O_26`0Kp`A(?DuJqS`Y-^4l!MBJgFjSA(SYd64A2t9Tove3^#B@;Pu0xC|@- z$^LJFw6SWfHZFyB)?x%oQXwq(GGy*hi`&D@FkG);ke?T!P#ifgwZgK ztp+KdYe1440ZH#|Ao=5Ya3T02=mGmd+UHLo#c3uM(|!v;8s7xcy6=OS!ghe&00k{$Gr-gI}+LpnGQtuH}Y;dFp>H<5oK zRz6GcPv?;Kq;vHQ3h7;nLjI<+QG`Oiqx_?1Es7U~&cjL+df;|xr)=P_OjYQPq5I8w zDA(4b%tE0&As;x;VpPc2%TXvMUqPXV@?#B3F$(#f&L2Gi6!P7dsi7W<1wHdo$e)i> zLp^j3DK@K6ILbpPKiju3%!9>h*EQN%N{JaEq)Qa$S8rg|!IX*pg`d@kMAFc;7^}4tke6zv z<~ABB@-pzyD`q6z0py*s)g6clEs0PfV?;t4x~{5$M}#gCT8zh2us6g}gq{?>f?{Q` zO24Qcj)pW7zD0h(zgkQrdqhxK0MY4B_Id#oOaH|swc?6|svl-5aG{;jW!;JfwAhwwa4HjSnbwIU7BV|e} z>>NZL2V6?mT5#S>ZZz4pV5OdHiJEjk$Suwl0h{bYs{~-!4vy;94b#zl^3YZ-O1FYD zY#&OBYdsX@Q+y+Pr}#$p!U#$rwR1f(c=BqpgEG4@WtupTDM}90fG=dIg>B=QP5-rs zB(6yxqvg^I?nXGJ@>r7&D$SHC^0uMw$%jsa|4)M8q@Iw11iyESa&PPpQI_YFl`i%$0?)SbL2!6^03To zmba;V<@^WJ#x8ehs5HdKHi|^FX*)Nz;yRgSc$ReP5$TfE#`w0Itk6fOBW3zo`T+>eU$ z#*!g%-=o1Y_Zr5e3g?DLy-sL}P%{hOTki?^#q0nlSxv%t4qp!5@~{_6lSlwRuPH9s zP`oa{HwJ3!%B!miYO3qKB?%$FH@vu6@pZCr!Ga}T{1z_ZMa%sQ7tLLO9!&Js!UB?_ z<~9&i}JHXG&8Y(JyRdHp2uMAWcmshMQ zE-kGM)YYxVR6UZ?BEnlQv=%?FD6U%9K)S80s8|z=`iw+mbwOo$WnfJEBHx075<@o; z8*lygIFvPU*UiV(5Y<=kP_vdu3UgJUp~SnPc5EUkgBPqfpofewPP3mcYmAx&?&n(P zh}FyIJ6~#DnL~Z}QtRzc?;ii9mcHIL!uo!lzBE7oR_@jO8UET{KG*a^0hz;D$oKTdzf(rrQhU^fz2- zxr?4)wVKdz+!L%)b)LC?`1S0Ku?~B6Z$AG!)a*ei70MmFg+C}fbx_*i36>SgsZw8A zW8GK!uJP>gw#)Xw!CP%tJ;747_hcWmUmM(ywbpRvRo<1cr1Cx~g0-EuhRNOlmO6D3 zlY5_K-s{+B4RNwOchNdc3TDsd`!nrOcFYMrIS(CO?^{DoogB*BE_ZeFoolW)vA(y{fA4AY1RJ15`VcxgUa^J-PV&wX7)yVg zV#K>g?FK1L4f+CFr@nw{>@T1S`wQssDc*VY*sYEe6f<7|nI#=5ozb^1cQ$>UTmKl` zk9Han%U!VNS$q3-_U!LEg{=qoSY14K0R8`534&zASrjY{Yd&G zT-23;Y+IIN0&=$-M%Zx8P))}WnV6K`={vmtP^R^8t1ap*W$0H`BHVr4&IL8rbG~P^ zcOd^8TfYp?UV*@Lnk?DVm2r$$3eu(>gVHTlp4=(5$PV%;ofQ1b?dLMB?MKEsYI6l4?38siqxSJ91D%`i&Gh^veM_2J$*y;n@z}{&xRGD50Y4BX{-Q_vCR&+mX-S zvCow}<|MXnr|rvgP{K~!NeO;mGlzKgSi8Jo?lIl9taBuL#_EL<*%Q)XFpv%|E&$r! zodye!++FPhjA)PF^j?R|T;~Xxx+mYf4))e}A zW`NPlpJxwJdes%$EmBe*PB@G*LGB3UYierJJIVUfuica%4KPg_ByU%HOJ|965Zr5D zbqMq1iBd8IIJe)u=33)S=s9B@zA9l>0#<=yD{bc zZ@+-XeFnH+0p!22Yn0Qg#J$JqR&;L2GSpqQ=)K`*{kxR|-F|p{oVuRiM*Hk4ZF8fz LzO7!?f1LdbIN1RX literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.po b/vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.po new file mode 100644 index 0000000000..eeebe5ca69 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/mm-ko-comp.euc-kr.po @@ -0,0 +1,1633 @@ +# Translation of SuSE patches included in gnome-patch-translation. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2006-06-26 19:23+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=EUC-KR\n" +"Content-Transfer-Encoding: 8bit\n" + +#: NetworkManager/gnome/applet/applet.c:286 +msgid "Network configuration could not be run" +msgstr "" + +#: NetworkManager/gnome/applet/applet.c:1799 +msgid "Dialup configuration could not be run" +msgstr "" + +#: NetworkManager/gnome/applet/applet.c:1845 +#, fuzzy +msgid "Configure _Modem..." +msgstr "VPN ϱ(_C)..." + +#: NetworkManager/gnome/applet/applet.c:1849 +#, fuzzy +msgid "Configure _ISDN..." +msgstr "VPN ϱ(_C)..." + +#: NetworkManager/gnome/applet/applet.glade.h:21 +#, fuzzy +msgid "Configure _Networking" +msgstr "Ʈũ (_N)" + +#: control-center-2.0/capplets/accessibility/at-properties/at-properties.desktop.in.in.h:1 +#, fuzzy +msgid "Assistive Technology" +msgstr " " + +#: control-center-2.0/capplets/accessibility/keyboard/accessibility-keyboard.desktop.in.in.h:1 +#, fuzzy +msgid "Accessibility" +msgstr "ټ (_A)" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties-structs.c:61 +msgid "Nautilus" +msgstr "" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties-structs.c:63 +msgid "gFTP" +msgstr "" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties-structs.c:88 +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties-structs.c:89 +#, fuzzy +msgid "Mozilla News" +msgstr "Mozilla" + +#. FIXME: Pan doesd not yet support %s +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties-structs.c:92 +msgid "Pan" +msgstr "" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties-structs.c:93 +msgid "TIN" +msgstr "" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties.glade.h:9 +#, fuzzy +msgid "Default FTP Browser" +msgstr "⺻ " + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties.glade.h:11 +#, fuzzy +msgid "Default News Reader" +msgstr "⺻ б α׷" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties.glade.h:19 +msgid "FTP" +msgstr "" + +#: control-center-2.0/capplets/default-applications/gnome-default-applications-properties.glade.h:21 +#, fuzzy +msgid "News" +msgstr " (_N)" + +#: control-center-2.0/capplets/font/font-properties.desktop.in.in.h:1 +#, fuzzy +msgid "Fonts" +msgstr "۲" + +#: control-center-2.0/capplets/keybindings/gnome-keybinding-properties.c:743 +msgid "Only special multimedia keys can be bound to this action!" +msgstr "" + +#: control-center-2.0/capplets/keybindings/keybinding.desktop.in.in.h:2 +#, fuzzy +msgid "Shortcuts" +msgstr "ٷ " + +#: control-center-2.0/capplets/network/gnome-network-preferences.desktop.in.in.h:1 +#, fuzzy +msgid "Network Proxies" +msgstr "Ʈũ Ͻ" + +#: control-center-2.0/capplets/network/gnome-network-preferences.glade.h:4 +msgid "Use the s_ystem's proxy settings" +msgstr "" + +#: control-center-2.0/capplets/passwd/gnome-passwd.desktop.in.in.h:2 +#, fuzzy +msgid "Change your password" +msgstr " ٲٱ" + +#: control-center-2.0/capplets/passwd/gnome-passwd.c:107 +#, fuzzy +msgid "New Password empty" +msgstr " (_N):" + +#: control-center-2.0/capplets/passwd/gnome-passwd.c:109 +#, fuzzy +msgid "Passwords match" +msgstr " ʹ ªϴ" + +#: control-center-2.0/capplets/passwd/gnome-passwd.c:113 +#, fuzzy +msgid "Passwords do not match" +msgstr " ʹ ªϴ" + +#: control-center-2.0/capplets/passwd/gnome-passwd.c:142 +#: control-center-2.0/capplets/passwd/gnome-passwd.glade.h:3 +#, no-c-format +msgid "Changing Password for User '%s'" +msgstr "" + +#: control-center-2.0/capplets/passwd/gnome-passwd.c:185 +msgid "" +"You have got capslock on!\n" +"Passwords are case-sensitive." +msgstr "" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:105 +#, fuzzy +msgid "" +"Could not start helper program.\n" +"Could not change password" +msgstr " ϴ" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:110 +#: ../capplets/passwd/pam-passwd.c:114 +#: control-center-2.0/capplets/passwd/pam-passwd.c:119 +#: ../capplets/passwd/pam-passwd.c:123 +#: control-center-2.0/capplets/passwd/pam-passwd.c:128 +#: ../capplets/passwd/pam-passwd.c:132 +#: control-center-2.0/capplets/passwd/pam-passwd.c:170 +#: ../capplets/passwd/pam-passwd.c:176 +#, fuzzy +msgid "" +"Unknown error while changing password.\n" +"Could not change password" +msgstr " ٲٷ ٲٱ⸦ ʽÿ." + +#: control-center-2.0/capplets/passwd/pam-passwd.c:152 +msgid "Password changed successfully" +msgstr "" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:155 +#, fuzzy +msgid "Old password doesn't match. Please try again." +msgstr " ùٸ ʽϴ, ٽ ԷϽʽÿ" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:158 +msgid "" +"Password is insecure.\n" +"Please choose a new password." +msgstr "" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:161 +msgid "" +"Password confirmation doesn't match New Password.\n" +"Please retype new password and confirmation" +msgstr "" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:164 +msgid "Protocol error" +msgstr "" + +#: control-center-2.0/capplets/passwd/pam-passwd.c:242 +#, c-format +msgid "" +"Success:\n" +"%s" +msgstr "" + +#: control-center-2.0/capplets/passwd/gnome-passwd.glade.h:5 +#, fuzzy +msgid "Password confirmation empty" +msgstr " (_N):" + +#: control-center-2.0/capplets/passwd/gnome-passwd.glade.h:6 +#, fuzzy +msgid "_Confirm Password:" +msgstr " Ȯ:" + +#: control-center-2.0/capplets/passwd/gnome-passwd.glade.h:7 +#, fuzzy +msgid "_New Password:" +msgstr " :" + +#: control-center-2.0/capplets/passwd/gnome-passwd.glade.h:8 +#, fuzzy +msgid "_Old Password:" +msgstr " (_S):" + +#: control-center-2.0/capplets/sound/sound-properties.glade.h:1 +msgid "E_nable software sound mixing (ESD)" +msgstr "" + +#: control-center-2.0/capplets/sound/sound-properties.glade.h:5 +#, fuzzy +msgid "Sounds" +msgstr "Ҹ" + +#: control-center-2.0/capplets/sound/sound-properties.glade.h:6 +#, fuzzy +msgid "System Beep" +msgstr "ý Ҹ" + +#: control-center-2.0/capplets/sound/sound-properties.glade.h:7 +#, fuzzy +msgid "_Enable system beep" +msgstr " ϱ(_E)" + +#: control-center-2.0/capplets/sound/sound-properties.glade.h:8 +msgid "_Play system sounds" +msgstr "" + +#: control-center-2.0/capplets/sound/sound-properties.glade.h:9 +msgid "_Visual system beep" +msgstr "" + +#: control-center-2.0/libsounds/sound-view.c:42 +msgid "Login" +msgstr "" + +#: control-center-2.0/libsounds/sound-view.c:42 +#, fuzzy +msgid "Logout" +msgstr "α׾ƿ" + +#: control-center-2.0/libsounds/sound-view.c:42 +msgid "Boing" +msgstr "" + +#: control-center-2.0/libsounds/sound-view.c:42 +#, fuzzy +msgid "Siren" +msgstr "ȭ" + +#: control-center-2.0/libsounds/sound-view.c:42 +msgid "Clink" +msgstr "" + +#: control-center-2.0/libsounds/sound-view.c:42 +#, fuzzy +msgid "Beep" +msgstr " " + +#: control-center-2.0/libsounds/sound-view.c:42 +#, fuzzy +msgid "No sound" +msgstr "Ҹ" + +#: control-center-2.0/libsounds/sound-view.c:115 +#, fuzzy +msgid "Sound not set for this event." +msgstr "Ȳ Ҹ(_S)" + +#: control-center-2.0/libsounds/sound-view.c:123 +#, fuzzy +msgid "" +"The sound file for this event does not exist.\n" +"You may want to install the gnome-audio packagefor a set of default sounds." +msgstr "" +" Ȳ ʽϴ.\n" +"⺻ ġϷ gnome-audio Ű\n" +"ġؾ մϴ." + +#: control-center-2.0/libsounds/sound-view.c:235 +#, fuzzy +msgid "Select sound file..." +msgstr "Ҹ " + +#: dia/plug-ins/cairo/diacairo.c:1066 +msgid "Cairo Portable Document Format" +msgstr "" + +#: dia/plug-ins/cairo/diacairo.c:1083 +msgid "Cairo PNG (with alpha)" +msgstr "" + +#: dia/plug-ins/cairo/diacairo.c:1092 +msgid "Cairo WMF" +msgstr "" + +#: dia/plug-ins/cairo/diacairo.c:1101 +msgid "Cairo old WMF" +msgstr "" + +#: dia/plug-ins/cairo/diacairo.c:1110 +msgid "Cairo Clipboard" +msgstr "" + +#: dia/plug-ins/xfig/xfig-import.c:451 +#, c-format +msgid "Color index %d too high, only 512 colors allowed. Using black instead." +msgstr "" + +#: dia/plug-ins/xfig/xfig-import.c:714 +#, c-format +msgid "Depth %d of of range, only 0-%d allowed.\n" +msgstr "" + +#: dia/plug-ins/xfig/xfig-import.c:1364 +#, c-format +msgid "Color number %d out of range 0..%d. Discarding color.\n" +msgstr "" + +#: eel-2.0/eel/eel-open-with-dialog.c:655 +#, fuzzy +msgid "Potential Applications " +msgstr "α׷ " + +#: eel-2.0/eel/eel-open-with-dialog.c:666 +#, fuzzy +msgid "All Applications" +msgstr "α׷ մϴ" + +#: file-roller/src/ui.h:55 +#, fuzzy +msgid "Delete file from the archive" +msgstr " Ͽ ϴ" + +#: gdm/daemon/slave.c:2101 +#, fuzzy +msgid "You must authenticate as root to shut down." +msgstr " Ϸ root ԷϽʽÿ." + +#: gdm/daemon/slave.c:2121 +#, fuzzy +msgid "You must authenticate as root to restart the computer." +msgstr " Ϸ root ԷϽʽÿ." + +#: gdm/daemon/verify-pam.c:344 +#, fuzzy +msgid "Your account is disabled. Please contact your system administrator" +msgstr " Ѿϴ. ý ڿ Ͻʽÿ" + +#: gdm/daemon/verify-pam.c:1037 +msgid "" +"\n" +"Your account has been disabled." +msgstr "" + +#: gdm/daemon/verify-pam.c:1039 +#, fuzzy +msgid "" +"\n" +"Your account has expired." +msgstr "%s Ǿϴ" + +#: gdm/gui/greeter/greeter_canvas_item.c:379 +#, fuzzy +msgid "" +msgstr "(_O): " + +#: gdm/gui/greeter/greeter_parser.c:1129 +#, fuzzy +msgid "Domain:" +msgstr ":" + +#: gedit/data/gedit.schemas.in.h:16 +#, fuzzy +msgid "Enable Document Info Plugin" +msgstr "gedit: ÷" + +#: gedit/data/gedit.schemas.in.h:18 +msgid "" +"Enable the Document Information plugin, which provides statistics about the " +"current document, such as the number of words." +msgstr "" + +#. Translators: This is the sorted list of encodings used by gedit +#. for auto-detecting the encoding of a file. "CURRENT" is the current locale encoding. +#. Only recognized encodings are used. +#: gedit/data/gedit.schemas.in.h:110 +msgid "[UTF-8,CURRENT,ISO-8859-15,UTF-16]" +msgstr "[UTF-8,CURRENT,EUC-KR,ISO-8859-1,UTF-16]" + +#: gedit/gedit/gedit-document.c:2079 +#, c-format +msgid "" +"This file has less than %d lines. Setting the cursor to last line of the " +"file." +msgstr "" + +#: gnome-applets-2.0/battstat/GNOME_BattstatApplet.xml.h:1 +msgid "Power Management _Settings..." +msgstr "" + +#: gnome-applets-2.0/battstat/battstat_applet.c:510 +#, c-format +msgid "" +"You have an unknown amount of battery power remaining (%d%% of the total " +"capacity)." +msgstr "" + +#: gnome-applets-2.0/battstat/properties.c:264 +#, c-format +msgid "" +"Could not run Power Management " +"Settings!\n" +"\n" +"%s" +msgstr "" + +#: gnome-applets-2.0/modemlights/GNOME_ModemLightsApplet.server.in.in.h:2 +#: gnome-applets-2.0/modemlights/modemlights.c:129 +#: ../modemlights/modemlights.c:1594 +msgid "Modem Lights" +msgstr " " + +#: gnome-applets-2.0/modemlights/modemlights.c:131 +msgid "" +"Released under the GNU general public license.\n" +"A modem status indicator and dialer.\n" +"Lights in order from the top or left are Send data and Receive data." +msgstr "" +"GNU General Public License ˴ϴ.\n" +" ¸ ְ ȭ ɾݴϴ.\n" +" Ȥ ·κ ̵ ͸ ް ִ° ݴϴ." + +#: gnome-applets-2.0/modemlights/modemlights.c:512 +msgid "" +"You are currently connected.\n" +"Do you want to disconnect?" +msgstr "" +" Ǿ ֽϴ\n" +" ڽϱ?" + +#: gnome-applets-2.0/modemlights/modemlights.c:582 +#, c-format +msgid "%#.1fMb received / %#.1fMb sent / time: %.1d:%.2d" +msgstr "%#.1fMb /%#.1fMb / ð: %.1d:%.2d" + +#: gnome-applets-2.0/modemlights/modemlights.c:587 +msgid "not connected" +msgstr "Ǿ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:1 +msgid "Ask for confirmation when connecting/disconnecting" +msgstr "/ Ȯ մϴ" + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:2 +msgid "Blink when connecting" +msgstr " Դϴ" + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:3 +msgid "Command executed when connecting" +msgstr " ɾ" + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:4 +msgid "Command executed when disconnecting" +msgstr " ɾ" + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:5 +msgid "Display a confirmation dialog when connecting or disconnecting." +msgstr "/ Ȯ ȭ ڸ Դϴ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:6 +msgid "Make the applet blink when the modem is connecting." +msgstr " ø Դϴ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:7 +msgid "Modem device name" +msgstr " ġ ̸" + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:8 +msgid "Modem lock file" +msgstr " " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:9 +msgid "Receive background color" +msgstr "ޱ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:10 +msgid "Receive foreground color" +msgstr "ޱ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:11 +msgid "Send background color" +msgstr " " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:12 +msgid "Send foreground color" +msgstr " " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:13 +msgid "Show connect time and throughput" +msgstr " ð ó ֱ" + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:14 +msgid "" +"Show extra information about the connect time and amount of data transmitted " +"and received." +msgstr " ð ۼ Ÿ ݴϴ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:15 +msgid "Status connected color" +msgstr " " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:16 +msgid "Status not connected color" +msgstr " " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:17 +msgid "Status waiting connection color" +msgstr " ٸ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:18 +msgid "Text background color" +msgstr "ؽƮ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:19 +msgid "Text foreground color" +msgstr "ؽƮ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:20 +msgid "Text outline color" +msgstr "ؽƮ ܰ " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:21 +msgid "The background color of the button used to indicate data received." +msgstr " Ÿ Ÿ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:22 +msgid "The background color of the button used to indicate data sent." +msgstr " Ÿ Ÿ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:23 +msgid "" +"The color used to display the status button when the modem is connected." +msgstr " Ǿ ߿ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:24 +msgid "" +"The color used to display the status button when the modem is connecting." +msgstr " ߿ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:25 +msgid "" +"The color used to display the status button when the modem is not connected." +msgstr " ƴ ߿ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:26 +msgid "The color used to indicate that data has been received." +msgstr "Ÿ ޾ ˸ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:27 +msgid "The color used to indicate that data has been sent." +msgstr "Ÿ ˸ ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:28 +msgid "The fraction of a second until the applet updates." +msgstr "ø Ʈϴ ( )." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:29 +msgid "The name of the modem device." +msgstr " ġ ̸." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:30 +msgid "The name of the modem lock file." +msgstr " ̸." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:32 +msgid "Use isdn" +msgstr "ISDN " + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:33 +msgid "Use isdn instead of ppp to connect the modem." +msgstr "𵩿 ϴ PPP ƴ϶ ISDN մϴ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:34 +msgid "Use this command to connect the modem." +msgstr "𵩿 ɾ մϴ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:35 +msgid "Use this command to disconnect the modem." +msgstr " ɾ մϴ." + +#: gnome-applets-2.0/modemlights/modemlights.schemas.in.h:36 +msgid "Verify owner of lock file" +msgstr " Ȯ" + +#: gnome-applets-2.0/modemlights/properties.c:423 +msgid "Modem Lights Preferences" +msgstr " ⺻ " + +#: gnome-applets-2.0/modemlights/properties.c:451 +msgid "U_pdate every:" +msgstr "Ʈ(_P): " + +#: gnome-applets-2.0/modemlights/properties.c:468 +msgid "seconds" +msgstr "" + +#. extra info checkbox +#: gnome-applets-2.0/modemlights/properties.c:476 +msgid "Sho_w connect time and throughput" +msgstr " ð ó ֱ(_W)" + +#: gnome-applets-2.0/modemlights/properties.c:486 +msgid "B_link connection status when connecting" +msgstr " ̱(_L)" + +#: gnome-applets-2.0/modemlights/properties.c:496 +msgid "Connections" +msgstr "" + +#: gnome-applets-2.0/modemlights/properties.c:505 +msgid "Co_nnection command:" +msgstr " ɾ(_N):" + +#: gnome-applets-2.0/modemlights/properties.c:531 +msgid "_Disconnection command:" +msgstr " ɾ(_D):" + +#. confirmation checkbox +#: gnome-applets-2.0/modemlights/properties.c:553 +msgid "Con_firm connection" +msgstr " Ȯ(_F)" + +#: gnome-applets-2.0/modemlights/properties.c:572 +msgid "Receive Data" +msgstr " ޱ" + +#: gnome-applets-2.0/modemlights/properties.c:578 +msgid "_Foreground:" +msgstr "ڻ(_F):" + +#: gnome-applets-2.0/modemlights/properties.c:583 +msgid "Send Data" +msgstr " " + +#: gnome-applets-2.0/modemlights/properties.c:588 +msgid "Foregroun_d:" +msgstr "ڻ(_D):" + +#: gnome-applets-2.0/modemlights/properties.c:590 +msgid "Backg_round:" +msgstr "(_R):" + +#: gnome-applets-2.0/modemlights/properties.c:593 +msgid "Connection Status" +msgstr " " + +#: gnome-applets-2.0/modemlights/properties.c:601 +msgid "Co_nnected:" +msgstr "(_N):" + +#: gnome-applets-2.0/modemlights/properties.c:603 +msgid "Disconnec_ted:" +msgstr " (_D):" + +#: gnome-applets-2.0/modemlights/properties.c:606 +msgid "C_onnecting:" +msgstr "(_O):" + +#: gnome-applets-2.0/modemlights/properties.c:617 +msgid "For_eground:" +msgstr "ڻ(_E):" + +#: gnome-applets-2.0/modemlights/properties.c:619 +msgid "Bac_kground:" +msgstr "(_K):" + +#: gnome-applets-2.0/modemlights/properties.c:621 +msgid "O_utline:" +msgstr "ܰ(_U):" + +#: gnome-applets-2.0/modemlights/properties.c:633 +msgid "Modem Options" +msgstr " ɼ" + +#: gnome-applets-2.0/modemlights/properties.c:642 +msgid "_Device:" +msgstr "ġ(_D):" + +#: gnome-applets-2.0/modemlights/properties.c:668 +msgid "_Lock file:" +msgstr " (_L):" + +#: gnome-applets-2.0/modemlights/properties.c:689 +msgid "_Verify owner of lock file" +msgstr " Ȯ(_V)" + +#. ISDN checkbox +#: gnome-applets-2.0/modemlights/properties.c:700 +msgid "U_se ISDN" +msgstr "ISDN (_S)" + +#: gnome-applets-2.0/modemlights/properties.c:717 +msgid "Advanced" +msgstr "" + +#: gnome-menus/desktop-directories/Development.directory.in.h:1 +#, fuzzy +msgid "Software Development" +msgstr "Ʈ " + +#: gnome-menus/desktop-directories/Gnomecc-Hardware.directory.in.h:1 +msgid "Hardware" +msgstr "" + +#: gnome-menus/desktop-directories/Gnomecc-Hardware.directory.in.h:2 +msgid "Hardware Settings" +msgstr "" + +#: gnome-menus/desktop-directories/Gnomecc-LookAndFeel.directory.in.h:1 +msgid "Appearance of the desktop" +msgstr "" + +#: gnome-menus/desktop-directories/Gnomecc-LookAndFeel.directory.in.h:2 +msgid "Look and Feel" +msgstr "" + +#: gnome-menus/desktop-directories/Gnomecc-Personal.directory.in.h:1 +msgid "Personal" +msgstr "" + +#: gnome-menus/desktop-directories/Gnomecc-System.directory.in.h:1 +#: gnome-system-monitor/src/interface.c:1158 +#, fuzzy +msgid "System" +msgstr "ý " + +#: gnome-menus/desktop-directories/Gnomecc-System.directory.in.h:2 +msgid "System Settings" +msgstr "" + +#. translators: use %l even in 24 hour locales, +#. * there is a switch in preferences. +#. +#: gnome-panel-2.0/applets/clock/clock.c:337 +#, fuzzy +msgid "" +"%l:%M\n" +"%S %p" +msgstr "%p %I:%M:%S" + +#: gnome-panel-2.0/applets/clock/clock.c:337 +#, fuzzy +msgid "" +"%l:%M\n" +"%p" +msgstr "%p %I:%M" + +#. translators: reverse the order of these arguments +#. * if the time should come before the +#. * date on a clock in your locale. +#. +#: gnome-panel-2.0/applets/clock/clock.c:343 +#, fuzzy +msgid "" +"%H:%M\n" +"%S" +msgstr "%H:%M:%S" + +#: gnome-panel-2.0/applets/clock/clock.c:344 +#, fuzzy +msgid "" +"%a\n" +"%b %e" +msgstr "%b %e (%a)" + +#: gnome-panel-2.0/applets/clock/clock.c:349 +#, fuzzy +msgid "" +"%l\n" +"%M\n" +"%S\n" +"%p" +msgstr "%p %I:%M:%S" + +#: gnome-panel-2.0/applets/clock/clock.c:349 +#, fuzzy +msgid "" +"%l\n" +"%M\n" +"%p" +msgstr "%p %I:%M" + +#: gnome-panel-2.0/applets/clock/clock.c:351 +#, fuzzy +msgid "" +"%H\n" +"%M\n" +"%S" +msgstr "%H:%M:%S" + +#: gnome-panel-2.0/applets/clock/clock.c:351 +#, fuzzy +msgid "" +"%H\n" +"%M" +msgstr "%H:%M" + +#: gnome-panel-2.0/applets/clock/clock.c:352 +#, fuzzy +msgid "" +"%a\n" +"%b\n" +"%e" +msgstr "%b %e (%a)" + +#: gnome-panel-2.0/gnome-panel/panel-addto.c:128 +msgid "Traditional Main Menu" +msgstr "" + +#: gnome-panel-2.0/gnome-panel/panel-addto.c:129 +#, fuzzy +msgid "The traditional GNOME menu" +msgstr "׳ ָ޴" + +#: gnome-panel-2.0/gnome-panel/panel-context-menu.c:163 +#: gnome-panel-2.0/gnome-panel/panel-context-menu.c:294 +#, fuzzy +msgid "_Lock Panel Postion" +msgstr "гο ױ(_L)" + +#: gnome-panel-2.0/gnome-panel/panel-context-menu.c:163 +#: gnome-panel-2.0/gnome-panel/panel-context-menu.c:294 +msgid "_Allow Panel to be Moved" +msgstr "" + +#: gnome-panel-2.0/gnome-panel/panel-global.schemas.in.h:10 +#: gnome-panel-2.0/gnome-panel/panel-toplevel.schemas.in.h:9 +msgid "" +"Disable support for moving a panel with a mouse drag. It has been know to " +"cause problems for users that accidentally move or resize their panels." +msgstr "" + +#: gnome-panel-2.0/gnome-panel/panel-global.schemas.in.h:22 +#, fuzzy +msgid "Lock Panel Position" +msgstr "г " + +#: gnome-panel-2.0/gnome-panel/panel-recent.c:57 +#, c-format +msgid "%s does not exist." +msgstr "" + +#: gnome-panel-2.0/gnome-panel/panel-toplevel.schemas.in.h:26 +msgid "Lock the panel position" +msgstr "" + +#: gnome-session-2.0/gnome-session/logout.c:482 +#, fuzzy +msgid "_Suspend the computer" +msgstr "ǻ ٽ (_R)" + +#: gnome-session-2.0/gnome-session/session-properties-capplet.c:125 +msgid "Enable" +msgstr "" + +#: gnome-session-2.0/gnome-session/session-properties-capplet.c:131 +#: gnome-session-2.0/gnome-session/session-properties-capplet.c:472 +msgid "Disable" +msgstr " ʱ" + +#: gnome-system-monitor/gnome-system-monitor.desktop.in.in.h:1 +#, fuzzy +msgid "GNOME System Monitor" +msgstr "ý " + +#. hardware section +#: gnome-system-monitor/src/interface.c:458 +msgid "Hardware" +msgstr "" + +#: gnome-system-monitor/src/interface.c:479 +#, fuzzy +msgid "Memory:" +msgstr "޸" + +#: gnome-system-monitor/src/interface.c:499 +#, fuzzy, c-format +msgid "Processor %d:" +msgstr "μ ʵ" + +#: gnome-system-monitor/src/interface.c:504 +#, fuzzy +msgid "Processor:" +msgstr "μ" + +#. disk space section +#: gnome-system-monitor/src/interface.c:524 +msgid "System Status" +msgstr "" + +#: gnome-system-monitor/src/interface.c:545 +msgid "User Space Free:" +msgstr "" + +#: gnome-utils-2.0/gnome-screenshot/screenshot-xfer.c:161 +#, fuzzy, c-format +msgid "" +"Insufficient permissions to save the file in:\n" +"%s" +msgstr "÷ %s ġ ϴ." + +#: gnome-vfs-2.0/libgnomevfs/gnome-vfs-filesystem-type.c:68 +#, fuzzy +msgid "SubMount Volume" +msgstr "SuperMount " + +#: gnome-vfs-2.0/libgnomevfs/gnome-vfs-volume-ops.c:471 +msgid "Could not get a DBus connection" +msgstr "" + +#: gnome-vfs-2.0/libgnomevfs/gnome-vfs-volume-ops.c:486 +#, fuzzy +msgid "Could not create dbus message" +msgstr " ϴ" + +#: gnome-vfs-2.0/libgnomevfs/gnome-vfs-volume-ops.c:506 +#, fuzzy +msgid "Could not append args to dbus message" +msgstr "ֿ ã ϴ" + +#: gnome-vfs-2.0/libgnomevfs/gnome-vfs-volume-ops.c:520 +msgid "Could not append args args to dbus message" +msgstr "" + +#: gnome-vfs-2.0/libgnomevfs/gnome-vfs-volume-ops.c:528 +#, fuzzy +msgid "Operation failed" +msgstr "۵ ҵǾϴ" + +#: gnome-vfs-2.0/modules/network-method.c:1523 +msgid "Novell Services" +msgstr "" + +#: gtk+/gtk/gtkfilesel.c:1753 +msgid "Home" +msgstr "" + +#: gtk+/gtk/gtkfilesel.c:1763 +msgid "Desktop" +msgstr "" + +#: gtk+/gtk/gtkfilesel.c:1777 +msgid "Documents" +msgstr "" + +#: gtk20/gtk/gtkfilechooserdefault.c:1652 +#: nautilus/src/nautilus-places-sidebar.c:159 +msgid "Search" +msgstr "" + +#. Accessible object name for the file chooser's shortcuts pane +#: gtk20/gtk/gtkfilechooserdefault.c:3537 +msgid "Places" +msgstr "" + +#. Column header for the file chooser's shortcuts pane +#: gtk20/gtk/gtkfilechooserdefault.c:3591 +#, fuzzy +msgid "_Places" +msgstr "ٲٱ(_R)" + +#: gtk20/gtk/gtkfilechooserdefault.c:4642 +#, fuzzy +msgid "Type a file name" +msgstr "߸ ̸" + +#: gtk20/gtk/gtkfilechooserdefault.c:7559 +#, fuzzy +msgid "Could not start the search process" +msgstr " ϴ" + +#: gtk20/gtk/gtkfilechooserdefault.c:7560 +msgid "" +"The program was not able to create a connection to the Beagle daemon. " +"Please make sure Beagle is running." +msgstr "" + +#: gtk20/gtk/gtkfilechooserdefault.c:7773 +#, fuzzy +msgid "Could not send the search request" +msgstr " ϴ" + +#: gtk20/gtk/gtkfilechooserdefault.c:8419 +#, fuzzy, c-format +msgid "%.1f KB" +msgstr "%.1f K" + +#: gtk20/gtk/gtkfilechooserdefault.c:8421 +#, fuzzy, c-format +msgid "%.1f MB" +msgstr "%.1f M" + +#: gtk20/gtk/gtkfilechooserdefault.c:8423 +#, fuzzy, c-format +msgid "%.1f GB" +msgstr "%.1f G" + +#: gtk20/gtk/gtkfilesel.c:778 +#, fuzzy +msgid "D_esktop" +msgstr " ȭ" + +#: gtk20/gtk/gtkfilesel.c:800 +#, fuzzy +msgid "Docu_ments" +msgstr " ۼ" + +#. These are the commonly used font styles, listed here only for +#. translations. +#: gtk20/gtk/gtkfontsel.c:78 +msgid "Ultra-Light" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:79 +msgid "Light" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:80 +msgid "Medium" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:81 +#, fuzzy +msgid "Normal" +msgstr " ũ(_N)" + +#: gtk20/gtk/gtkfontsel.c:82 +msgid "Regular" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:83 +#, fuzzy +msgid "Italic" +msgstr "Ӳ(_I)" + +#: gtk20/gtk/gtkfontsel.c:84 +msgid "Oblique" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:85 +msgid "Semi-Bold" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:86 +#, fuzzy +msgid "Bold" +msgstr "(_B)" + +#: gtk20/gtk/gtkfontsel.c:87 +msgid "Ultra-Bold" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:88 +msgid "Heavy" +msgstr "" + +#: gtk20/gtk/gtkfontsel.c:89 +#, fuzzy +msgid "Bold Italic" +msgstr "Ӳ(_I)" + +#: libgnomeui-2.0/file-chooser/gtkfilesystemgnomevfs.c:1207 +#, fuzzy +msgid "Network Servers" +msgstr "Ʈũ Ͻ" + +#: metacity/src/metacity.schemas.in.h:18 +msgid "If true, enables the Windows flag keys to show the panel's main menu" +msgstr "" + +#: metacity/src/metacity.schemas.in.h:19 +msgid "If true, horizontal viewport constraints are used" +msgstr "" + +#: metacity/src/metacity.schemas.in.h:21 +msgid "" +"If true, metacity will give the user feedback using window border effects." +msgstr "" + +#: metacity/src/metacity.schemas.in.h:23 +msgid "" +"If true, pressing a mouse button on a window will cause it to be raised to " +"the top of the stack. If false, windows must be raised explicitly by " +"clicking on their title bar." +msgstr "" + +#: metacity/src/metacity.schemas.in.h:25 +msgid "" +"If true, then pressing the Windows flag keys will cause the panel's main " +"menu to appear." +msgstr "" + +#: metacity/src/metacity.schemas.in.h:27 +msgid "If true, use window border effects" +msgstr "" + +#: metacity/src/metacity.schemas.in.h:28 +msgid "" +"If true, windows are not allowed to be horizontally moved outside the " +"viewport." +msgstr "" + +#: metacity/src/metacity.schemas.in.h:65 +msgid "Raise windows when a mouse button is pressed on them" +msgstr "" + +#: nautilus/libnautilus-private/apps_nautilus_preferences.schemas.in.h:49 +#, fuzzy +msgid "" +"If this is set to true, an icon linking to the Network Servers view will be " +"put on the desktop." +msgstr " ϸ, ȭ鿡 ϴ." + +#: nautilus/libnautilus-private/apps_nautilus_preferences.schemas.in.h:66 +#, fuzzy +msgid "Network Servers icon visible on the desktop" +msgstr " ȭ鿡 Ȩ Դϴ" + +#: nautilus/libnautilus-private/nautilus-desktop-link-monitor.c:133 +#, fuzzy, c-format +msgid "You cannot move the drive \"%s\" to the trash." +msgstr " \"%s\"() ű ϴ." + +#: nautilus/src/file-manager/fm-error-reporting.c:137 +#, c-format +msgid "" +"Couldn't rename \"%s\" to \"%s\". Please make sure the new name has only " +"valid characters in it." +msgstr "" + +#: nautilus/src/nautilus-navigation-window-menus.c:455 +#: nautilus/src/nautilus-spatial-window.c:818 +msgid "_Search" +msgstr "ã(_S)" + +#. name, stock id, label +#: nautilus/src/nautilus-navigation-window-menus.c:456 +#: nautilus/src/nautilus-spatial-window.c:819 +#, fuzzy +msgid "Search for files" +msgstr "ϸθ ã" + +#: nautilus/src/nautilus-places-sidebar.c:373 +#, fuzzy +msgid "Mount failed" +msgstr " ű " + +#: nautilus/src/nautilus-search-bar.c:181 +#, fuzzy +msgid "Search:" +msgstr "̸" + +#. name, stock id +#: nautilus/src/nautilus-window-menus.c:689 +#, fuzzy +msgid "_Network" +msgstr "Ʈũ" + +#. label, accelerator +#: nautilus/src/nautilus-window-menus.c:690 +#, fuzzy +msgid "Go to the network location" +msgstr "ǻ ġ ϴ" + +#: nautilus-share/src/nautilus-share.c:211 +#, c-format +msgid "" +"Nautilus needs to add some permissions to your folder \"%s\" in order to " +"share it" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:219 +#, c-format +msgid "" +"The folder \"%s\" needs the following extra permissions for sharing to " +"work:\n" +"%s%s%sDo you want Nautilus to add these permissions to the folder " +"automatically?" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:223 +msgid " - read permission by others\n" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:224 +msgid " - write permission by others\n" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:225 +msgid " - execute permission by others\n" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:229 +msgid "Add the permissions automatically" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:255 +#, c-format +msgid "Could not change the permissions of folder \"%s\"" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:525 +msgid "Share name is too long" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:559 +msgid "The share name cannot be empty" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:572 +#, c-format +msgid "Error while getting share information: %s" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:582 +msgid "Another share has the same name" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:728 +msgid "There was an error while getting the sharing information" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:820 +msgid "Modify _Share" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:822 +msgid "Create _Share" +msgstr "" + +#: nautilus-share/src/nautilus-share.c:1176 +msgid "Sharing Options" +msgstr "" + +#: nautilus-share/src/shares.c:122 +#, c-format +msgid "%s %s %s returned with signal %d" +msgstr "" + +#: nautilus-share/src/shares.c:131 +#, c-format +msgid "%s %s %s failed for an unknown reason" +msgstr "" + +#: nautilus-share/src/shares.c:151 +#, c-format +msgid "'net usershare' returned error %d: %s" +msgstr "" + +#: nautilus-share/src/shares.c:153 +#, c-format +msgid "'net usershare' returned error %d" +msgstr "" + +#: nautilus-share/src/shares.c:184 +msgid "the output of 'net usershare' is not in valid UTF-8 encoding" +msgstr "" + +#: nautilus-share/src/shares.c:418 ../src/shares.c:577 +msgid "Failed" +msgstr "" + +#: nautilus-share/src/shares.c:512 +#, c-format +msgid "Samba's testparm returned with signal %d" +msgstr "" + +#: nautilus-share/src/shares.c:518 +msgid "Samba's testparm failed for an unknown reason" +msgstr "" + +#: nautilus-share/src/shares.c:533 +#, c-format +msgid "Samba's testparm returned error %d: %s" +msgstr "" + +#: nautilus-share/src/shares.c:535 +#, c-format +msgid "Samba's testparm returned error %d" +msgstr "" + +#: nautilus-share/src/shares.c:642 +#, c-format +msgid "Cannot remove the share for path %s: that path is not shared" +msgstr "" + +#: nautilus-share/src/shares.c:688 +msgid "" +"Cannot change the path of an existing share; please remove the old share " +"first and add a new one" +msgstr "" + +#: nautilus-share/interfaces/share-dialog.glade.in.h:2 +msgid "Co_mment:" +msgstr "" + +#: nautilus-share/interfaces/share-dialog.glade.in.h:3 +msgid "Share _name:" +msgstr "" + +#: nautilus-share/interfaces/share-dialog.glade.in.h:4 +msgid "Share this _folder" +msgstr "" + +#: nautilus-share/interfaces/share-dialog.glade.in.h:5 +msgid "_Allow other people to write in this folder" +msgstr "" + +#: shared-mime-info/freedesktop.org.xml.in.h:317 +#, fuzzy +msgid "WMA audio" +msgstr "WAV " + +#: xchat/src/common/cfgfiles.c:729 +#, fuzzy +msgid "" +"* Running IRC as root is not recommended! You should\n" +" create a User Account and use that to login.\n" +msgstr "" +"* IRC Ʈ ߽ϴ!!\n" +" ο īƮ ϰ װ ̿Ͻʽÿ.\n" + +#: xchat/src/fe-gtk/setup.c:209 +#, fuzzy +msgid "Open an extra tab for outgoing msg" +msgstr " ˸ ǥմϴ." + +#: xmms/Effect/echo_plugin/gui.c:23 ../Effect/echo_plugin/gui.c:135 +#: xmms/Effect/stereo_plugin/stereo.c:56 ../Effect/stereo_plugin/stereo.c:120 +#: xmms/Effect/voice/about.c:35 ../General/ir/about.c:51 +#: xmms/General/ir/configure.c:205 ../General/ir/configure.c:376 +#: xmms/General/joystick/about.c:35 ../General/joystick/configure.c:272 +#: xmms/General/song_change/song_change.c:340 ../Input/cdaudio/cddb.c:854 +#: xmms/Input/cdaudio/cddb.c:862 ../Input/cdaudio/cddb.c:870 +#: xmms/Input/cdaudio/cddb.c:894 ../Input/cdaudio/configure.c:715 +#: xmms/Input/mikmod/plugin.c:125 ../Input/mikmod/plugin.c:616 +#: xmms/Input/mpg123/configure.c:616 ../Input/mpg123/fileinfo.c:188 +#: xmms/Input/mpg123/fileinfo.c:295 ../Input/mpg123/http.c:208 +#: xmms/Input/mpg123/mpg123.c:1165 ../Input/tonegen/tonegen.c:55 +#: xmms/Input/vorbis/configure.c:449 ../Input/vorbis/fileinfo.c:210 +#: xmms/Input/vorbis/http.c:208 ../Input/vorbis/vorbis.c:778 +#: xmms/Output/OSS/configure.c:497 ../Output/disk_writer/disk_writer.c:137 +#: xmms/Output/disk_writer/disk_writer.c:406 ../Output/esd/about.c:44 +#: xmms/Output/esd/configure.c:208 ../Output/solaris/about.c:24 +#: xmms/Output/solaris/configure.c:266 ../Output/sun/about.c:36 +#: xmms/Output/sun/configure.c:557 ../Output/alsa/about.c:46 +#: xmms/Output/alsa/configure.c:437 ../Visualization/blur_scope/config.c:101 +#: xmms/Visualization/opengl_spectrum/configure.c:73 +#: ../libxmms/dirbrowser.c:342 xmms/xmms/equalizer.c:1371 +#: ../xmms/equalizer.c:1377 ../xmms/equalizer.c:1441 +#: xmms/xmms/equalizer.c:1450 ../xmms/equalizer.c:1701 ../xmms/input.c:254 +#: xmms/xmms/main.c:3147 ../xmms/playlistwin.c:726 ../xmms/playlistwin.c:837 +#: xmms/xmms/playlistwin.c:1421 ../xmms/playlistwin.c:1474 +#: xmms/xmms/prefswin.c:313 ../xmms/prefswin.c:1207 ../xmms/util.c:582 +msgid "OK" +msgstr "Ȯ" + +#: xmms/General/song_change/song_change.c:238 +#, fuzzy +msgid "Commands" +msgstr "ɾ:" + +#: xmms/General/song_change/song_change.c:245 +#, fuzzy +msgid "Shell-command to run when xmms starts a new song." +msgstr " ɾ Էϼ." + +#: xmms/General/song_change/song_change.c:268 +#, fuzzy +msgid "Shell-command to run toward the end of a song." +msgstr " ɾ Էϼ." + +#: xmms/General/song_change/song_change.c:313 +#, c-format +msgid "" +"You can use the following format strings which will be substituted before " +"calling the command (not all are useful for the end-of-playlist command).\n" +"\n" +"%%F: Frequency (in hertz)\n" +"%%c: Number of channels\n" +"%%f: filename (full path)\n" +"%%l: length (in milliseconds)\n" +"%%n or %%s: Song name\n" +"%%r: Rate (in bits per second)\n" +"%%t: Playlist position (%%02d)\n" +"%%p: Currently playing (1 or 0)" +msgstr "" + +#: xmms/Input/cdaudio/configure.c:339 +#, c-format +msgid "Directory %s exists, but you do not have permission to access it." +msgstr "" + +#: xmms/Input/mikmod/plugin.c:582 +msgid "Always use filename as title" +msgstr "" + +#: xmms/Input/mpg123/configure.c:593 +msgid "Override default ID3V2 encoding" +msgstr "" + +#: xmms/Input/mpg123/configure.c:602 +msgid "Encoding name:" +msgstr "" + +#: xmms/Input/mpg123/fileinfo.c:507 +#, fuzzy +msgid "CCITT J.17" +msgstr "CCIT J.17" + +#: xmms/Input/vorbis/fileinfo.c:852 +#, fuzzy, c-format +msgid "Average bitrate: %.1f kbps" +msgstr " Ʈ: %d kbps" + +#: xmms/Input/vorbis/fileinfo.c:857 +#, c-format +msgid "Vendor: %s" +msgstr "" + +#: xmms/Output/OSS/about.c:31 +#, fuzzy +msgid "" +"XMMS OSS Driver\n" +"\n" +" This program is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation; either version 2 of the License, or\n" +"(at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n" +"USA.\n" +"\n" +"XMMS 3DSE patch release 11 for XMMS 1.2.5\n" +"Copyright (C) 2001 - Cornelis Frank\n" +"e-mail: \n" +"home page: http://studwww.rug.ac.be/~fcorneli/xmms" +msgstr "" +"XMMS ALSA ̹\n" +"\n" +" α׷ ƮԴϴ; в FSF GNU General\n" +"Public License ǰϿ α׷ Ǵ Ͻ \n" +"ֽϴ; GPL ̼ ι° Ǵ ( ÿ ǰ) \n" +".\n" +"\n" +" α׷ ϰ Ǿ ٶ, ׷ α׷\n" +"ǰ Ǵ Ư ɼ  \n" +"ʽϴ. ڼ GNU General Public License\n" +"Ͻñ ٶϴ.\n" +"\n" +"в GNU General Public License α׷ Բ\n" +"Դϴ; ׷ ʴٸ, the Free Software\n" +"Foundation, Inc,, 59 Template Place - Suite 330, Boston, NA 02111-1307,\n" +"USA Ͻñ ٶϴ.\n" +"Author: Matthieu Sozeau (mattam@altern.org)" + +#: xmms/Output/disk_writer/disk_writer.c:389 +msgid "Don't strip file name extension" +msgstr "" + +#: xmms/Output/alsa/configure.c:360 +#, fuzzy +msgid "Soundcard:" +msgstr "ī #%d - %s" + +#: xmms/Output/alsa/configure.c:402 +msgid "XMMS:" +msgstr "" + +#: xmms/xmms/about.c:49 +msgid "Ian 'Hixie' Hickson" +msgstr "" + +#: xmms/xmms/main.c:137 +msgid "/Time Display (MMM:SS)" +msgstr "" + +#: xmms/xmms/main.c:273 +#, fuzzy +msgid "/Play" +msgstr "" + +#: xmms/xmms/main.c:274 +#, fuzzy +msgid "/Play/Play File" +msgstr "/ " + +#: xmms/xmms/main.c:275 +#, fuzzy +msgid "/Play/Play Directory" +msgstr "/丮 " + +#: xmms/xmms/main.c:276 +#, fuzzy +msgid "/Play/Play Location" +msgstr "/ġ " + +#: xmms/xmms/main.c:277 +#, fuzzy +msgid "/Play/Play AudioCD" +msgstr "//" + +#. I18N: -Q, --queue switch +#: xmms/xmms/main.c:3410 +#, c-format +msgid "Add file(s) to playlist and queue" +msgstr "" + +#. I18N: Only "SWITCH" may be translated +#: xmms/xmms/main.c:3411 ../xmms/main.c:3415 ../xmms/main.c:3419 +msgid "[=SWITCH]" +msgstr "" + +#. I18N: -S, --toggle-shuffle switch +#: xmms/xmms/main.c:3413 +#, c-format +msgid "Toggle the 'shuffle' flag." +msgstr "" + +#. I18N: -R, --toggle-repeat switch +#: xmms/xmms/main.c:3417 +#, c-format +msgid "Toggle the 'repeat' flag." +msgstr "" + +#. I18N: -A, --toggle-advance switch +#: xmms/xmms/main.c:3421 +#, c-format +msgid "Toggle the 'no playlist advance' flag." +msgstr "" + +#. I18N: "on" and "off" is not translated. +#: xmms/xmms/main.c:3424 +#, c-format +msgid "SWITCH may be either 'on' or 'off'\n" +msgstr "" + +#. I18N: -q, --quit switch +#: xmms/xmms/main.c:3436 +#, fuzzy, c-format +msgid "Close remote session." +msgstr " ȣƮ " + +#. +#. * I18N: "on" and "off" is not +#. * translated. +#. +#: xmms/xmms/main.c:3612 ../xmms/main.c:3636 ../xmms/main.c:3660 +#, c-format +msgid "Value '%s' not understood, must be either 'on' or 'off'.\n" +msgstr "" + +#: xmms/xmms/playlistwin.c:832 +#, c-format +msgid "" +"Unknown file type for %s.\n" +"The filename of the playlist should end in either \".m3u\" or \".pls\"." +msgstr "" + +#: xmms/xmms/prefswin.c:1036 +msgid "" +"When moving windows around, snap them together, and towards screen edges at " +"this distance" +msgstr "" + +#: xmms/xmms/prefswin.c:1058 +#, fuzzy +msgid "" +"Recommended if you want to load playlists that were created in MS Windows" +msgstr "MS  ָ о̰ " + +#: xmms/xmms/prefswin.c:1074 +msgid "Store information such as song title and length to playlists" +msgstr "" + +#: xmms/xmms/prefswin.c:1152 +#, fuzzy +msgid "Advanced Title Options" +msgstr " " + +#: xmms/xmms/prefswin.c:1160 +msgid "" +"%0.2n - Display a 0 padded 2 char long tracknumber\n" +"%!p(...) - Display what's inside parentheses if Performer (%p) is not set\n" +"%?p(...) - Display what's inside parentheses if Performer (%p) is set\n" +"\n" +"For more details, please read the included README or http://www.xmms.org/" +"docs/readme.php" +msgstr "" + +#: xmms/xmms/prefswin.c:1178 +#, fuzzy +msgid "Audio CD directory" +msgstr "/ϱ/丮" + +#: xmms/xmms/prefswin.c:1196 +msgid "Audio CD" +msgstr "" + +#: xmms/xmms/skinwin.c:206 +msgid "(system default)" +msgstr "" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/mm-ko.euc-kr.mo b/vendor/github.com/chai2010/gettext-go/testdata/mm-ko.euc-kr.mo new file mode 100644 index 0000000000000000000000000000000000000000..d43c8697f139c317d429a09a1122f7942e0e678a GIT binary patch literal 11965 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, 2000. +#: app/sheets.c:453 +msgid "" +msgstr "" +"Project-Id-Version: dia 0.85\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-08-18 18:13+0200\n" +"PO-Revision-Date: 2000-05-31 10:16:35+0900\n" +"Last-Translator: Young-Ho Cha \n" +"Language-Team: Korean \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=euc-kr\n" +"Content-Transfer-Encoding: 8-bit\n" + +#: app/app_procs.c:219 +#, c-format +msgid "%s error: don't know how to export into %s\n" +msgstr "" + +#: app/app_procs.c:228 +#, c-format +msgid "%s error: input and output file name is identical: %s" +msgstr "" + +#: app/app_procs.c:235 +#, fuzzy, c-format +msgid "%s error: need valid input file %s\n" +msgstr "ùٸ Է ʿմϴ\n" + +#. if (!quiet) +#: app/app_procs.c:255 +#, c-format +msgid "%s --> %s\n" +msgstr "" + +#: app/app_procs.c:307 +#, fuzzy, c-format +msgid "Can't find output format %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#. Translators: The argument is a list of options, not to be translated +#: app/app_procs.c:401 +#, fuzzy, c-format +msgid "Export to file format and exit. Supported formats are: %s" +msgstr "о ϰ Ĩϴ" + +#. &export_file_name +#: app/app_procs.c:410 +msgid "Export loaded file and exit" +msgstr "о ϰ Ĩϴ" + +#: app/app_procs.c:410 +msgid "OUTPUT" +msgstr "" + +#. &export_file_format +#: app/app_procs.c:412 +msgid "FORMAT" +msgstr "" + +#: app/app_procs.c:415 +#, fuzzy +msgid "Export graphics size" +msgstr "о ϰ Ĩϴ" + +#: app/app_procs.c:415 +msgid "WxH" +msgstr "" + +#: app/app_procs.c:417 +msgid "Don't show the splash screen" +msgstr "" + +#: app/app_procs.c:419 +msgid "Send error messages to stderr instead of showing dialogs." +msgstr "" + +#: app/app_procs.c:421 +msgid "Display credits list and exit" +msgstr "" + +#: app/app_procs.c:423 +msgid "Display version and exit" +msgstr "" + +#: app/app_procs.c:424 +msgid "Show this help message" +msgstr " ݴϴ" + +#: app/app_procs.c:465 +msgid "Can't connect to session manager!\n" +msgstr " ڿ Ҽ ϴ!\n" + +#. TRANSLATOR: 2nd and 3rd %s are time and date respectively. +#: app/app_procs.c:492 +#, c-format +msgid "Dia version %s, compiled %s %s\n" +msgstr "" + +#: app/app_procs.c:494 +#, c-format +msgid "Dia version %s\n" +msgstr "" + +#: app/app_procs.c:548 app/app_procs.c:550 +msgid "" +"Couldn't find standard objects when looking for object-libs, exiting...\n" +msgstr "object-libs ã ǥ ü ã Ͽϴ, մϴ...\n" + +#: app/app_procs.c:591 +#, fuzzy +msgid "Diagram1.dia" +msgstr "ǥ " + +#: app/app_procs.c:633 +msgid "" +"This shouldn't happen. Please file a bug report at bugzilla.gnome.org\n" +"describing how you can cause this message to appear.\n" +msgstr "" + +#. no standard buttons +#: app/app_procs.c:645 +#, fuzzy +msgid "" +"Modified diagrams exist.\n" +"Are you sure you want to quit Dia\n" +"without saving them?" +msgstr "" +" ǥ ֽϴ.\n" +" Ͻðڽϱ?" + +#: app/app_procs.c:649 +#, fuzzy +msgid "Quit Dia" +msgstr " α׷" + +#. This printf seems to prevent a race condition with unrefs. +#. Yuck. -Lars +#: app/app_procs.c:700 +msgid "Thank you for using Dia.\n" +msgstr "" + +#: app/app_procs.c:716 app/app_procs.c:723 +msgid "Could not create per-user Dia config directory" +msgstr "ٸ Dia 丮 Ҽ ϴ" + +#: app/app_procs.c:725 +msgid "" +"Could not create per-user Dia config directory. Please make sure that the " +"environment variable HOME points to an existing directory." +msgstr "" + +#: app/app_procs.c:747 +msgid "Objects and filters internal to dia" +msgstr "Dia ü " + +#: app/app_procs.c:786 +msgid "[OPTION...] [FILE...]" +msgstr "[ɼ...] [...]" + +#: app/app_procs.c:789 +#, c-format +msgid "" +"Error on option %s: %s.\n" +"Run '%s --help' to see a full list of available command line options.\n" +msgstr "" +"ɼ %s ֽϴ : %s. \n" +"'%s --help' Ѽ ɼ ü Ͻʽÿ.\n" + +#: app/app_procs.c:874 +msgid "" +"The original author of Dia was:\n" +"\n" +msgstr "" + +#: app/app_procs.c:879 +msgid "" +"\n" +"The current maintainers of Dia are:\n" +"\n" +msgstr "" + +#: app/app_procs.c:884 +msgid "" +"\n" +"Other authors are:\n" +"\n" +msgstr "" + +#: app/app_procs.c:889 +msgid "" +"\n" +"Dia is documented by:\n" +"\n" +msgstr "" + +#: app/autosave.c:93 +msgid "Recovering autosaved diagrams" +msgstr "" + +#: app/autosave.c:101 +msgid "" +"Autosaved files exist.\n" +"Please select those you wish to recover." +msgstr "" + +#: app/color_area.c:317 app/color_area.c:364 +#, fuzzy +msgid "Select foreground color" +msgstr " :" + +#: app/color_area.c:318 app/color_area.c:365 +#, fuzzy +msgid "Select background color" +msgstr " :" + +#: app/commands.c:134 +#, fuzzy, c-format +msgid "Diagram%d.dia" +msgstr "ǥ " + +#: app/commands.c:205 +msgid "No existing object to paste.\n" +msgstr "ٿ ü ϴ.\n" + +#: app/commands.c:529 app/commands.c:567 +msgid "Could not find help directory" +msgstr "" + +#: app/commands.c:536 +#, fuzzy, c-format +msgid "" +"Could not open help directory:\n" +"%s" +msgstr "`%s' ϴ" + +#. +#. * Translators should localize the following string +#. * which will give them credit in the About box. +#. * E.g. "Fulano de Tal " +#. +#: app/commands.c:606 +msgid "translator_credits-PLEASE_ADD_YOURSELF_HERE" +msgstr "" + +#: app/commands.c:620 dia.desktop.in.h:1 +#, fuzzy +msgid "Dia" +msgstr "ȭ" + +#: app/commands.c:622 +msgid "Copyright (C) 1998-2002 The Free Software Foundation and the authors" +msgstr "" + +#: app/commands.c:623 +#, fuzzy +msgid "" +"Dia is a program for drawing structured diagrams.\n" +"Please visit http://www.gnome.org/projects/dia for more information." +msgstr "" +" ˰ http://www.lysator.liu.se/~alla/dia Ͻʽ" +"" + +#: app/commands.c:662 +msgid "About Dia" +msgstr " α׷" + +#: app/commands.c:705 +#, c-format +msgid "Dia v %s by Alexander Larsson" +msgstr "" + +#. Exact spelling is Chépélov (using *ML entities) +#: app/commands.c:711 +msgid "Maintainers: Lars Clausen and Cyrille Chepelov" +msgstr "" + +#: app/commands.c:715 +#, fuzzy +msgid "Please visit http://www.gnome.org/projects/dia for more information" +msgstr "" +" ˰ http://www.lysator.liu.se/~alla/dia Ͻʽ" +"" + +#: app/commands.c:720 +msgid "Contributors:" +msgstr "ֽźе:" + +#: app/defaults.c:43 app/defaults.c:146 +msgid "Object defaults" +msgstr "⺻ ü" + +#: app/defaults.c:61 +msgid "This object has no defaults." +msgstr " ü ⺻ ϴ." + +#: app/defaults.c:111 +#, fuzzy +msgid "Defaults: " +msgstr "⺻ " + +#: app/dia-props.c:89 +#, fuzzy +msgid "Diagram Properties" +msgstr "/ȭ/Ӽ(_P)" + +#: app/dia-props.c:123 +msgid "Dynamic grid" +msgstr "" + +#: app/dia-props.c:131 +msgid "x" +msgstr "" + +#: app/dia-props.c:135 +msgid "y" +msgstr "" + +#: app/dia-props.c:140 +#, fuzzy +msgid "Spacing" +msgstr "" + +#: app/dia-props.c:160 +#, fuzzy +msgid "Visible spacing" +msgstr " (_V)" + +#. Hexes! +#: app/dia-props.c:181 app/preferences.c:162 +msgid "Hex grid" +msgstr "" + +#: app/dia-props.c:189 +#, fuzzy +msgid "Hex grid size" +msgstr " ũ:" + +#: app/dia-props.c:202 +#, fuzzy +msgid "Grid" +msgstr ":" + +#: app/dia-props.c:213 lib/diagramdata.c:127 +msgid "Background" +msgstr "" + +#: app/dia-props.c:224 app/preferences.c:105 +msgid "Grid Lines" +msgstr " " + +#: app/dia-props.c:235 +#, fuzzy +msgid "Page Breaks" +msgstr " :" + +#: app/dia-props.c:246 +#, fuzzy +msgid "Colors" +msgstr "ݱ" + +#. Can we be sure that the filename is the 'proper title'? +#: app/dia-props.c:265 +#, fuzzy, c-format +msgid "Diagram Properties: %s" +msgstr "/ȭ/Ӽ(_P)" + +#: app/dia_embedd.c:352 +msgid "Could not initialize Bonobo!" +msgstr "Bonobo ʱȭ Ҽ ϴ!" + +#: app/diacanvas.c:121 +#, fuzzy +msgid "X position" +msgstr "ռ" + +#: app/diacanvas.c:122 +msgid "X position of child widget" +msgstr "" + +#: app/diacanvas.c:131 +#, fuzzy +msgid "Y position" +msgstr "ռ" + +#: app/diacanvas.c:132 +msgid "Y position of child widget" +msgstr "" + +#: app/diagram_tree_menu.c:45 +#, fuzzy +msgid "/_Sort objects" +msgstr "帧 ü" + +#: app/diagram_tree_menu.c:46 +#, fuzzy +msgid "/Sort objects/by _name" +msgstr "ü ޴ " + +#: app/diagram_tree_menu.c:48 +msgid "/Sort objects/by _type" +msgstr "" + +#: app/diagram_tree_menu.c:50 +msgid "/Sort objects/as _inserted" +msgstr "" + +#: app/diagram_tree_menu.c:53 +msgid "/Sort objects/All by name" +msgstr "" + +#: app/diagram_tree_menu.c:55 +msgid "/Sort objects/All by type" +msgstr "" + +#: app/diagram_tree_menu.c:57 +msgid "/Sort objects/All as inserted" +msgstr "" + +#: app/diagram_tree_menu.c:59 +msgid "/Sort objects/_Default" +msgstr "" + +#: app/diagram_tree_menu.c:60 +msgid "/Sort objects/Default/by _name" +msgstr "" + +#: app/diagram_tree_menu.c:62 +msgid "/Sort objects/Default/by _type" +msgstr "" + +#: app/diagram_tree_menu.c:64 +msgid "/Sort objects/Default/as _inserted" +msgstr "" + +#: app/diagram_tree_menu.c:66 +#, fuzzy +msgid "/Sort _diagrams" +msgstr "ǥ μ" + +#: app/diagram_tree_menu.c:67 +msgid "/Sort _diagrams/by _name" +msgstr "" + +#: app/diagram_tree_menu.c:69 +msgid "/Sort _diagrams/as _inserted" +msgstr "" + +#: app/diagram_tree_menu.c:71 +#, fuzzy +msgid "/Sort diagrams/_Default" +msgstr "ǥ:" + +#: app/diagram_tree_menu.c:72 +msgid "/Sort diagrams/Default/by _name" +msgstr "" + +#: app/diagram_tree_menu.c:74 +msgid "/Sort diagrams/Default/as _inserted" +msgstr "" + +#: app/diagram_tree_menu.c:82 app/diagram_tree_menu.c:93 +#, fuzzy +msgid "/_Locate" +msgstr "÷:" + +#: app/diagram_tree_menu.c:83 +#, fuzzy +msgid "/_Properties" +msgstr "(_P)" + +#: app/diagram_tree_menu.c:84 +msgid "/_Hide this type" +msgstr "" + +#: app/diagram_tree_window.c:76 +#, fuzzy +msgid "Diagram tree" +msgstr "ǥ " + +#: app/dialogs.c:51 +msgid "Ok" +msgstr "" + +#: app/dialogs.c:52 app/layer_dialog.c:1006 app/paginate_psprint.c:291 +msgid "Cancel" +msgstr "" + +#. paper size +#: app/diapagelayout.c:116 +msgid "Paper Size" +msgstr " ũ" + +#. orientation +#: app/diapagelayout.c:149 +msgid "Orientation" +msgstr "" + +#. margins +#: app/diapagelayout.c:187 +msgid "Margins" +msgstr "" + +#: app/diapagelayout.c:199 +msgid "Top:" +msgstr ":" + +#: app/diapagelayout.c:212 +msgid "Bottom:" +msgstr "Ʒ:" + +#: app/diapagelayout.c:225 +msgid "Left:" +msgstr ":" + +#: app/diapagelayout.c:238 +msgid "Right:" +msgstr ":" + +#. Scaling +#: app/diapagelayout.c:252 +msgid "Scaling" +msgstr "" + +#: app/diapagelayout.c:263 +msgid "Scale:" +msgstr ":" + +#: app/diapagelayout.c:275 +msgid "Fit to:" +msgstr ":" + +#: app/diapagelayout.c:287 +msgid "by" +msgstr "" + +#: app/diapagelayout.c:682 +#, c-format +msgid "%0.3gcm x %0.3gcm" +msgstr "" + +#: app/diapagelayout.c:786 app/pagesetup.c:76 +msgid "Page Setup" +msgstr " " + +#: app/disp_callbacks.c:81 app/properties.c:152 +msgid "" +"This object doesn't support Undo/Redo.\n" +"Undo information erased." +msgstr "" +" ü / ʽϴ.\n" +" ϴ." + +#: app/disp_callbacks.c:116 +#, fuzzy +msgid "Properties..." +msgstr "(_P)" + +#: app/disp_callbacks.c:919 +msgid "" +"The object you dropped cannot fit into its parent. \n" +"Either expand the parent object, or drop the object elsewhere." +msgstr "" + +#: app/display.c:95 +msgid "Diagram modified!" +msgstr "ǥ Ǿϴ!" + +#: app/display.c:983 +msgid "" +msgstr "" + +#: app/display.c:985 +#, fuzzy, c-format +msgid "" +"The diagram '%s'\n" +"has not been saved. Save changes now?" +msgstr "" +"ǥ ʾҽϴ.\n" +" â ݰڽϱ?" + +#: app/display.c:996 +#, fuzzy +msgid "Close Diagram" +msgstr "ǥ " + +#: app/display.c:1001 +msgid "Discard Changes" +msgstr "" + +#: app/export_png.c:134 app/load_save.c:904 app/render_eps.c:103 +#: plug-ins/cairo/diacairo.c:913 plug-ins/cgm/cgm.c:1157 +#: plug-ins/dxf/dxf-export.c:505 plug-ins/hpgl/hpgl.c:732 +#: plug-ins/metapost/render_metapost.c:964 +#: plug-ins/pstricks/render_pstricks.c:800 plug-ins/shape/shape-export.c:135 +#: plug-ins/svg/render_svg.c:152 plug-ins/wpg/wpg.c:1066 +#: plug-ins/xfig/xfig-export.c:1100 plug-ins/xslt/xslt.c:94 +#, fuzzy, c-format +msgid "Can't open output file %s: %s\n" +msgstr "`%s' ϴ" + +#: app/export_png.c:142 +msgid "Could not create PNG write structure" +msgstr "PNG Ҽ ϴ" + +#: app/export_png.c:151 +msgid "Could not create PNG header info structure" +msgstr "PNG Ҽ ϴ" + +#: app/export_png.c:159 +msgid "Error occurred while writing PNG" +msgstr "PNG ߻߽ϴ" + +#. Create a dialog +#: app/export_png.c:299 +#, fuzzy +msgid "PNG Export Options" +msgstr " " + +#. Translators: Menu item Verb/Channel/Export +#. Translators: Menu item Verb/Channel/Export/Export +#: app/export_png.c:300 objects/FS/function.c:685 objects/FS/function.c:687 +msgid "Export" +msgstr "" + +#: app/export_png.c:305 +#, fuzzy +msgid "Image width:" +msgstr "׸ :" + +#: app/export_png.c:308 +#, fuzzy +msgid "Image height:" +msgstr "׸ :" + +#: app/export_png.c:354 +msgid "Portable Network Graphics" +msgstr "" + +#: app/filedlg.c:122 app/filedlg.c:404 +msgid "By extension" +msgstr "Ȯ" + +#: app/filedlg.c:184 +msgid "Open Diagram" +msgstr "ǥ " + +#: app/filedlg.c:215 +msgid "Open Options" +msgstr " ϱ" + +#: app/filedlg.c:223 app/filedlg.c:526 +msgid "Determine file type:" +msgstr " :" + +#: app/filedlg.c:259 +msgid "" +"Some characters in the filename are neither UTF-8 nor your local encoding.\n" +"Some things will break." +msgstr "" + +#: app/filedlg.c:265 app/filedlg.c:448 +#, c-format +msgid "" +"The file '%s' already exists.\n" +"Do you want to overwrite it?" +msgstr "" +" '%s' ֽϴ.\n" +" ڽϱ?" + +#: app/filedlg.c:273 app/filedlg.c:455 +msgid "File already exists" +msgstr " ̹ ֽϴ" + +#: app/filedlg.c:306 +msgid "Save Diagram" +msgstr "ǥ " + +#. Need better way to make it a reasonable size. Isn't there some +#. standard look for them (or is that just Gnome?) +#: app/filedlg.c:311 +#, fuzzy +msgid "Compress diagram files" +msgstr " :" + +#: app/filedlg.c:320 +msgid "" +"Compression reduces file size to less than 1/10th size and speeds up loading " +"and saving. Some text programs cannot manipulate compressed files." +msgstr "" + +#: app/filedlg.c:474 +#, c-format +msgid "" +"Could not determine which export filter\n" +"to use to save '%s'" +msgstr "" + +#: app/filedlg.c:493 +msgid "Export Diagram" +msgstr "" + +#: app/filedlg.c:518 +msgid "Export Options" +msgstr "" + +#: app/interface.c:53 +msgid "Modify object(s)" +msgstr "ü " + +#: app/interface.c:54 +msgid "Modify" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Magnify +#: app/interface.c:58 app/interface.c:59 objects/FS/function.c:975 +msgid "Magnify" +msgstr "Ȯ" + +#: app/interface.c:63 +msgid "Scroll around the diagram" +msgstr "ǥ " + +#: app/interface.c:64 +msgid "Scroll" +msgstr "" + +#: app/interface.c:68 app/interface.c:69 lib/properties.c:77 +#: lib/properties.h:516 lib/properties.h:519 objects/UML/activity.c:122 +#: objects/UML/actor.c:120 objects/UML/classicon.c:141 +#: objects/UML/component.c:126 objects/UML/component_feature.c:154 +#: objects/UML/node.c:125 objects/UML/note.c:119 objects/UML/object.c:145 +#: objects/UML/small_package.c:126 objects/UML/state.c:139 +#: objects/UML/usecase.c:136 objects/Jackson/requirement.c:144 +#: objects/network/basestation.c:131 objects/network/radiocell.c:146 +msgid "Text" +msgstr "ڿ" + +#: app/interface.c:73 app/interface.c:74 +msgid "Box" +msgstr "" + +#: app/interface.c:78 app/interface.c:79 +msgid "Ellipse" +msgstr "Ÿ" + +#: app/interface.c:83 app/interface.c:84 +msgid "Polygon" +msgstr "ٰ" + +#: app/interface.c:88 app/interface.c:89 +#, fuzzy +msgid "Beziergon" +msgstr "" + +#: app/interface.c:93 app/interface.c:94 objects/standard/line.c:238 +msgid "Line" +msgstr "" + +#: app/interface.c:98 app/interface.c:99 +msgid "Arc" +msgstr "ȣ" + +#: app/interface.c:103 app/interface.c:104 +msgid "Zigzagline" +msgstr "" + +#: app/interface.c:108 app/interface.c:109 +msgid "Polyline" +msgstr "ἱ" + +#: app/interface.c:113 app/interface.c:114 +msgid "Bezierline" +msgstr "" + +#: app/interface.c:118 app/interface.c:119 +msgid "Image" +msgstr "׸" + +#: app/interface.c:391 +#, fuzzy +msgid "Diagram menu." +msgstr "ǥ:" + +#: app/interface.c:429 +msgid "Pops up the Navigation window." +msgstr "" + +#: app/interface.c:511 +msgid "Zoom" +msgstr "Ȯ" + +#: app/interface.c:525 +msgid "Toggles snap-to-grid for this window." +msgstr "" + +#: app/interface.c:588 +msgid "NULL tooldata in tool_select_update" +msgstr "" + +#: app/interface.c:1071 +msgid "" +"Foreground & background colors for new objects. The small black and white " +"squares reset colors. The small arrows swap colors. Double click to change " +"colors." +msgstr "" + +#: app/interface.c:1086 +msgid "" +"Line widths. Click on a line to set the default line width for new " +"objects. Double-click to set the line width more precisely." +msgstr "" + +#: app/interface.c:1126 +msgid "" +"Arrow style at the beginning of new lines. Click to pick an arrow, or set " +"arrow parameters with Details..." +msgstr "" + +#: app/interface.c:1131 +msgid "" +"Line style for new lines. Click to pick a line style, or set line style " +"parameters with Details..." +msgstr "" + +#: app/interface.c:1145 +msgid "" +"Arrow style at the end of new lines. Click to pick an arrow, or set arrow " +"parameters with Details..." +msgstr "" + +#: app/interface.c:1255 +msgid "Diagram Editor" +msgstr "ǥ " + +#: app/layer_dialog.c:70 +msgid "New Layer" +msgstr " " + +#: app/layer_dialog.c:71 +msgid "Raise Layer" +msgstr " ø" + +#: app/layer_dialog.c:72 +msgid "Lower Layer" +msgstr " " + +#: app/layer_dialog.c:73 +msgid "Delete Layer" +msgstr " " + +#: app/layer_dialog.c:212 +msgid "Layers" +msgstr "" + +#: app/layer_dialog.c:226 +msgid "Diagrams:" +msgstr "ǥ:" + +#: app/layer_dialog.c:271 +msgid "Close" +msgstr "ݱ" + +#: app/layer_dialog.c:331 +msgid "New layer" +msgstr " " + +#: app/layer_dialog.c:537 +msgid "none" +msgstr "" + +#: app/layer_dialog.c:968 +msgid "Edit Layer Attributes" +msgstr " Ӽ " + +#: app/layer_dialog.c:987 +msgid "Layer name:" +msgstr " ̸:" + +#: app/layer_dialog.c:996 app/paginate_psprint.c:283 +msgid "OK" +msgstr "Ȯ" + +#: app/linewidth_area.c:246 lib/properties.h:480 lib/properties.h:483 +#: objects/chronogram/chronoline.c:181 objects/chronogram/chronoref.c:160 +msgid "Line width" +msgstr " " + +#: app/linewidth_area.c:258 +msgid "Line width:" +msgstr " :" + +#: app/load_save.c:260 +msgid "" +"Error loading diagram.\n" +"Linked object not found in document." +msgstr "" +"ǥ д .\n" +" ü ã ϴ." + +#: app/load_save.c:263 +msgid "" +"Error loading diagram.\n" +"connection handle does not exist." +msgstr "" + +#: app/load_save.c:280 +#, fuzzy, c-format +msgid "" +"Error loading diagram.\n" +"connection point %s does not exist." +msgstr "" +"ǥ д .\n" +" ϴ." + +#: app/load_save.c:305 +#, fuzzy, c-format +msgid "Can't find parent %s of %s object\n" +msgstr "ǥ ü" + +#: app/load_save.c:350 +msgid "You must specify a file, not a directory.\n" +msgstr "" + +#: app/load_save.c:357 plug-ins/dxf/dxf-import.c:1304 plug-ins/wpg/wpg.c:1170 +#: plug-ins/xfig/xfig-import.c:1560 plug-ins/xslt/xslt.c:87 +#, c-format +msgid "Couldn't open: '%s' for reading.\n" +msgstr "" + +#: app/load_save.c:374 app/load_save.c:379 +#, c-format +msgid "" +"Error loading diagram %s.\n" +"Unknown file type." +msgstr "" + +#: app/load_save.c:386 +#, c-format +msgid "" +"Error loading diagram %s.\n" +"Not a Dia file." +msgstr "" + +#: app/load_save.c:586 +#, c-format +msgid "" +"Error loading diagram:\n" +"%s.\n" +"A valid Dia file defines at least one layer." +msgstr "" + +#: app/load_save.c:937 +#, c-format +msgid "Failed to save file '%s'.\n" +msgstr "" + +#: app/load_save.c:1008 app/load_save.c:1013 +#, fuzzy +msgid "Dia Diagram File" +msgstr "ǥ " + +#: app/menus.c:49 app/menus.c:84 +msgid "/_File" +msgstr "/(_F)" + +#: app/menus.c:51 app/menus.c:86 +msgid "/File/_New" +msgstr "" + +#: app/menus.c:53 app/menus.c:88 +#, fuzzy +msgid "/File/_Open..." +msgstr "// (_u)..." + +#. recent file list is dynamically inserted here +#: app/menus.c:55 app/menus.c:60 app/menus.c:64 app/menus.c:66 app/menus.c:95 +#: app/menus.c:99 +#, fuzzy +msgid "/File/---" +msgstr "/(_F)" + +#: app/menus.c:56 +#, fuzzy +msgid "/File/_Diagram tree" +msgstr "//ǥ μ(_P)..." + +#: app/menus.c:58 +msgid "/File/Sheets and Objects..." +msgstr "" + +#: app/menus.c:61 +msgid "/File/_Preferences..." +msgstr "" + +#: app/menus.c:63 +#, fuzzy +msgid "/File/P_lugins..." +msgstr "÷" + +#: app/menus.c:67 app/menus.c:103 +msgid "/File/_Quit" +msgstr "" + +#: app/menus.c:69 app/menus.c:230 +msgid "/_Help" +msgstr "" + +#: app/menus.c:71 app/menus.c:232 +msgid "/Help/_Manual" +msgstr "" + +#: app/menus.c:73 app/menus.c:234 +msgid "/Help/---" +msgstr "" + +#: app/menus.c:74 app/menus.c:235 +msgid "/Help/_About..." +msgstr "" + +#: app/menus.c:90 +msgid "/File/_Save" +msgstr "" + +#: app/menus.c:92 +msgid "/File/Save _As..." +msgstr "" + +#: app/menus.c:94 +msgid "/File/_Export..." +msgstr "" + +#: app/menus.c:96 +msgid "/File/Page Set_up..." +msgstr "// (_u)..." + +#: app/menus.c:97 +msgid "/File/_Print Diagram..." +msgstr "//ǥ μ(_P)..." + +#: app/menus.c:100 +msgid "/File/_Close" +msgstr "//ݱ(_C)" + +#: app/menus.c:105 +msgid "/_Edit" +msgstr "/(_E)" + +#: app/menus.c:107 +msgid "/Edit/_Undo" +msgstr "//(_U)" + +#: app/menus.c:109 +msgid "/Edit/_Redo" +msgstr "//(_R)" + +#: app/menus.c:111 app/menus.c:121 +#, fuzzy +msgid "/Edit/---" +msgstr "//ڸ" + +#: app/menus.c:112 +msgid "/Edit/_Copy" +msgstr "//(_C)" + +#: app/menus.c:114 +msgid "/Edit/C_ut" +msgstr "//ڸ(_u)" + +#: app/menus.c:116 +msgid "/Edit/_Paste" +msgstr "//̱(_P)" + +#: app/menus.c:118 +#, fuzzy +msgid "/Edit/_Duplicate" +msgstr "//(_D)" + +#: app/menus.c:119 +msgid "/Edit/_Delete" +msgstr "//(_D)" + +#: app/menus.c:122 +msgid "/Edit/Copy Text" +msgstr "//ڿ " + +#: app/menus.c:123 +msgid "/Edit/Cut Text" +msgstr "//ڿ ڸ" + +#: app/menus.c:124 +msgid "/Edit/Paste _Text" +msgstr "//ڿ ̱(_T)" + +#: app/menus.c:125 +#, fuzzy +msgid "/_Diagram" +msgstr "ǥ:" + +#: app/menus.c:127 +#, fuzzy +msgid "/Diagram/_Properties..." +msgstr "/ȭ/Ӽ(_P)" + +#: app/menus.c:128 +#, fuzzy +msgid "/Diagram/_Layers..." +msgstr "/ȭ/(_L)" + +#: app/menus.c:129 +msgid "/_View" +msgstr "/(_V)" + +#: app/menus.c:131 +msgid "/View/Zoom _In" +msgstr "//Ȯ(_I)" + +#: app/menus.c:133 +msgid "/View/Zoom _Out" +msgstr "//(_O)" + +#: app/menus.c:135 +msgid "/View/_Zoom" +msgstr "//Ȯ(_Z)" + +#: app/menus.c:137 +msgid "/View/Zoom/400%" +msgstr "//Ȯ/400%" + +#: app/menus.c:138 +msgid "/View/Zoom/283%" +msgstr "//Ȯ/283%" + +#: app/menus.c:139 +msgid "/View/Zoom/200%" +msgstr "//Ȯ/200%" + +#: app/menus.c:140 +msgid "/View/Zoom/141%" +msgstr "//Ȯ/141%" + +#: app/menus.c:141 +msgid "/View/Zoom/100%" +msgstr "//Ȯ/100%" + +#: app/menus.c:143 +msgid "/View/Zoom/85%" +msgstr "///85%" + +#: app/menus.c:144 +msgid "/View/Zoom/70.7%" +msgstr "///70.7%" + +#: app/menus.c:145 +msgid "/View/Zoom/50%" +msgstr "///50%" + +#: app/menus.c:146 +msgid "/View/Zoom/35.4%" +msgstr "///35.4%" + +#: app/menus.c:147 +msgid "/View/Zoom/25%" +msgstr "///25%" + +#: app/menus.c:148 app/menus.c:156 +#, fuzzy +msgid "/View/---" +msgstr "/(_V)" + +#: app/menus.c:150 +msgid "/View/_AntiAliased" +msgstr "//Ƽ̽(_A)" + +#: app/menus.c:152 +#, fuzzy +msgid "/View/Show _Grid" +msgstr "// (_A)" + +#: app/menus.c:153 +msgid "/View/_Snap To Grid" +msgstr "// " + +#: app/menus.c:154 +msgid "/View/Show _Rulers" +msgstr "// (_R)" + +#: app/menus.c:155 +msgid "/View/Show _Connection Points" +msgstr "// (_C)" + +#: app/menus.c:157 +msgid "/View/New _View" +msgstr "// (_V)" + +#. Show All, Best Fit. Same as the Gimp, Ctrl+E +#: app/menus.c:159 +msgid "/View/Show _All" +msgstr "// (_A)" + +#: app/menus.c:160 +#, fuzzy +msgid "/View/Re_draw" +msgstr "// (_V)" + +#: app/menus.c:161 +msgid "/_Objects" +msgstr "/ü(_O)" + +#: app/menus.c:163 +msgid "/Objects/Send to _Back" +msgstr "/ü/ڷ (_B)" + +#: app/menus.c:164 +msgid "/Objects/Bring to _Front" +msgstr "/ü/ (_F)" + +#: app/menus.c:165 +#, fuzzy +msgid "/Objects/Send Backwards" +msgstr "/ü/ڷ " + +#: app/menus.c:166 +#, fuzzy +msgid "/Objects/Bring Forwards" +msgstr "/ü/ " + +#: app/menus.c:167 app/menus.c:171 app/menus.c:175 app/menus.c:189 +#, fuzzy +msgid "/Objects/---" +msgstr "/ü(_O)" + +#: app/menus.c:168 +msgid "/Objects/_Group" +msgstr "/ü/(_G)" + +#. deliberately not using Ctrl+U for Ungroup +#: app/menus.c:170 +msgid "/Objects/_Ungroup" +msgstr "/ü/Ǯ(_U)" + +#: app/menus.c:172 +#, fuzzy +msgid "/Objects/_Parent" +msgstr "/ü/(_G)" + +#: app/menus.c:173 +#, fuzzy +msgid "/Objects/_Unparent" +msgstr "/ü/Ǯ(_U)" + +#: app/menus.c:174 +msgid "/Objects/_Unparent Children" +msgstr "" + +#: app/menus.c:176 +#, fuzzy +msgid "/Objects/Align" +msgstr "/ü/ (_V)" + +#: app/menus.c:178 +#, fuzzy +msgid "/Objects/Align/Left" +msgstr "/ü/ /" + +#: app/menus.c:179 +#, fuzzy +msgid "/Objects/Align/Center" +msgstr "/ü/ /߰" + +#: app/menus.c:180 +#, fuzzy +msgid "/Objects/Align/Right" +msgstr "/ü/ /" + +#: app/menus.c:181 +#, fuzzy +msgid "/Objects/Align/Top" +msgstr "/ü/ /" + +#: app/menus.c:182 +#, fuzzy +msgid "/Objects/Align/Middle" +msgstr "/ü/ (_V)" + +#: app/menus.c:183 +#, fuzzy +msgid "/Objects/Align/Bottom" +msgstr "/ü/ /Ʒ" + +#: app/menus.c:184 +#, fuzzy +msgid "/Objects/Align/---" +msgstr "/ü(_O)" + +#: app/menus.c:185 +#, fuzzy +msgid "/Objects/Align/Spread Out Horizontally" +msgstr "/ü/ (_H)" + +#: app/menus.c:186 +#, fuzzy +msgid "/Objects/Align/Spread Out Vertically" +msgstr "/ü/ (_V)" + +#: app/menus.c:187 +#, fuzzy +msgid "/Objects/Align/Adjacent" +msgstr "/ü/ /" + +#: app/menus.c:188 +#, fuzzy +msgid "/Objects/Align/Stacked" +msgstr "/ü/ (_V)" + +#: app/menus.c:190 +#, fuzzy +msgid "/Objects/_Properties..." +msgstr "ü Ӽ" + +#: app/menus.c:191 +#, fuzzy +msgid "/_Select" +msgstr "" + +#: app/menus.c:193 +msgid "/Select/All" +msgstr "//" + +#: app/menus.c:194 +msgid "/Select/None" +msgstr "//" + +#: app/menus.c:195 app/menus.c:208 +msgid "/Select/Invert" +msgstr "//ݴ" + +#: app/menus.c:196 +msgid "/Select/Connected" +msgstr "//" + +#: app/menus.c:197 +msgid "/Select/Transitive" +msgstr "" + +#: app/menus.c:198 +msgid "/Select/Same Type" +msgstr "// " + +#: app/menus.c:199 +#, fuzzy +msgid "/Select/---" +msgstr "" + +#: app/menus.c:200 +msgid "/Select/Replace" +msgstr "" + +#: app/menus.c:202 +msgid "/Select/Union" +msgstr "" + +#: app/menus.c:204 +msgid "/Select/Intersect" +msgstr "" + +#: app/menus.c:206 +msgid "/Select/Remove" +msgstr "//" + +#: app/menus.c:210 +#, fuzzy +msgid "/_Tools" +msgstr "//" + +#: app/menus.c:212 +msgid "/Tools/Modify" +msgstr "" + +#: app/menus.c:213 +msgid "/Tools/Magnify" +msgstr "" + +#: app/menus.c:214 +msgid "/Tools/Scroll" +msgstr "" + +#: app/menus.c:215 +msgid "/Tools/Text" +msgstr "//ڿ" + +#: app/menus.c:216 +msgid "/Tools/Box" +msgstr "//" + +#: app/menus.c:217 +msgid "/Tools/Ellipse" +msgstr "//Ÿ" + +#: app/menus.c:218 +msgid "/Tools/Polygon" +msgstr "//ٰ" + +#: app/menus.c:219 +#, fuzzy +msgid "/Tools/Beziergon" +msgstr "//" + +#: app/menus.c:220 app/menus.c:226 +#, fuzzy +msgid "/Tools/---" +msgstr "//" + +#: app/menus.c:221 +msgid "/Tools/Line" +msgstr "//" + +#: app/menus.c:222 +msgid "/Tools/Arc" +msgstr "//ȣ" + +#: app/menus.c:223 +msgid "/Tools/Zigzagline" +msgstr "//" + +#: app/menus.c:224 +msgid "/Tools/Polyline" +msgstr "//" + +#: app/menus.c:225 +msgid "/Tools/Bezierline" +msgstr "//" + +#: app/menus.c:227 +msgid "/Tools/Image" +msgstr "//׸" + +#: app/menus.c:228 +msgid "/_Input Methods" +msgstr "" + +#: app/menus.c:399 +msgid "NULL tooldata in tool_menu_select" +msgstr "" + +#: app/menus.c:497 +#, fuzzy +msgid "Diagram Menu" +msgstr "ǥ:" + +#: app/menus.c:632 +#, c-format +msgid "" +"Can't find menu entry '%s'!\n" +"This is probably a i18n problem (try LANG=C)." +msgstr "" + +#: app/modify_tool.c:328 +msgid "Couldn't get GTK settings" +msgstr "" + +#: app/paginate_psprint.c:242 +msgid "Select Printer" +msgstr " " + +#: app/paginate_psprint.c:254 sheets/ciscocomputer.sheet.in.h:25 +msgid "Printer" +msgstr "" + +#: app/paginate_psprint.c:268 sheets/Misc.sheet.in.h:1 +msgid "File" +msgstr "" + +#: app/paginate_psprint.c:377 +#, fuzzy, c-format +msgid "Could not run command '%s': %s" +msgstr "`%s' ϴ" + +#: app/paginate_psprint.c:380 +#, fuzzy, c-format +msgid "Could not open '%s' for writing: %s" +msgstr " ϴ: '%s' .\n" + +#: app/paginate_psprint.c:397 +#, c-format +msgid "Printing error: command '%s' returned %d\n" +msgstr "" + +#: app/paginate_psprint.c:408 +#, c-format +msgid "Printing error: command '%s' caused sigpipe." +msgstr "" + +#: app/plugin-manager.c:200 +msgid "Plug-ins" +msgstr "÷" + +#: app/plugin-manager.c:255 +#, fuzzy +msgid "Loaded" +msgstr "÷:" + +#: app/plugin-manager.c:262 objects/UML/class.c:112 +#: objects/UML/large_package.c:128 +#, fuzzy +msgid "Name" +msgstr "̸:" + +#: app/plugin-manager.c:268 +#, fuzzy +msgid "Description" +msgstr ":" + +#: app/plugin-manager.c:277 +#, fuzzy +msgid "Load at Startup" +msgstr "Ҷ ڵ б" + +#: app/plugin-manager.c:284 +#, fuzzy +msgid "File Name" +msgstr " ̸:" + +#: app/preferences.c:102 +msgid "User Interface" +msgstr " ̽" + +#: app/preferences.c:103 +#, fuzzy +msgid "Diagram Defaults" +msgstr "ǥ:" + +#: app/preferences.c:104 +msgid "View Defaults" +msgstr "⺻ " + +#: app/preferences.c:106 +#, fuzzy +msgid "Diagram Tree" +msgstr "ǥ:" + +#: app/preferences.c:120 +#, fuzzy +msgid "Reset tools after create" +msgstr " :" + +#: app/preferences.c:121 +#, fuzzy +msgid "Compress saved files" +msgstr " :" + +#: app/preferences.c:122 +msgid "Number of undo levels:" +msgstr "ϱ :" + +#: app/preferences.c:123 +msgid "" +"Reverse dragging selects\n" +"intersecting objects" +msgstr "" + +#: app/preferences.c:124 +msgid "Recent documents list size:" +msgstr "" + +#: app/preferences.c:125 +msgid "Use menu bar" +msgstr "" + +#: app/preferences.c:127 +msgid "Keep tool box on top of diagram windows" +msgstr "" + +#: app/preferences.c:129 +#, fuzzy +msgid "New diagram:" +msgstr " ǥ(_N)" + +#: app/preferences.c:130 +#, fuzzy +msgid "Portrait" +msgstr ":" + +#: app/preferences.c:132 +#, fuzzy +msgid "Paper type:" +msgstr " ũ" + +#: app/preferences.c:135 +#, fuzzy +msgid "Background Color:" +msgstr " :" + +#: app/preferences.c:138 +msgid "New window:" +msgstr " â:" + +#: app/preferences.c:139 +msgid "Width:" +msgstr ":" + +#: app/preferences.c:140 +msgid "Height:" +msgstr ":" + +#: app/preferences.c:141 +msgid "Magnify:" +msgstr "" + +#: app/preferences.c:144 +msgid "Connection Points:" +msgstr ":" + +#. { NULL, PREF_NONE, 0, NULL, 3, N_("Grid:") }, +#: app/preferences.c:145 app/preferences.c:149 app/preferences.c:155 +#, fuzzy +msgid "Visible" +msgstr " (_V)" + +#: app/preferences.c:148 +msgid "Page breaks:" +msgstr "" + +#: app/preferences.c:150 app/preferences.c:160 +#, fuzzy +msgid "Color:" +msgstr "ݱ" + +#: app/preferences.c:151 +#, fuzzy +msgid "Solid lines" +msgstr " " + +#: app/preferences.c:156 +#, fuzzy +msgid "Snap to" +msgstr " (_S)" + +#: app/preferences.c:157 +msgid "Dynamic grid resizing" +msgstr "" + +#: app/preferences.c:158 +msgid "X Size:" +msgstr "" + +#: app/preferences.c:159 +msgid "Y Size:" +msgstr "" + +#: app/preferences.c:161 +msgid "Lines per major line" +msgstr "" + +#: app/preferences.c:163 +#, fuzzy +msgid "Hex Size:" +msgstr "B " + +#: app/preferences.c:182 +#, fuzzy +msgid "Diagram tree window:" +msgstr "ǥ:" + +#: app/preferences.c:184 +#, fuzzy +msgid "Save hidden object types" +msgstr "ǥ ü" + +#: app/preferences.c:391 lib/prop_inttypes.c:158 +msgid "Yes" +msgstr "" + +#: app/preferences.c:391 app/preferences.c:402 lib/prop_inttypes.c:160 +#: lib/prop_inttypes.c:186 +msgid "No" +msgstr "ƴϿ" + +#: app/preferences.c:504 +msgid "Preferences" +msgstr "" + +#: app/properties.c:55 +msgid "Object properties" +msgstr "ü Ӽ" + +#: app/properties.c:80 +msgid "This object has no properties." +msgstr " ü Ӽ ϴ" + +#: app/properties.c:202 +#, fuzzy +msgid "Properties: " +msgstr "(_P)" + +#: app/properties.c:206 +#, fuzzy +msgid "Object properties:" +msgstr "ü Ӽ" + +#. Use the Plugins menu item to get a pointer to the File menu, +#. but any item on the File menu will do +#: app/recent_files.c:61 +#, fuzzy +msgid "/File/Plugins..." +msgstr "÷" + +#: app/recent_files.c:70 app/recent_files.c:143 +msgid "/File/Quit" +msgstr "" + +#: app/render_eps.c:145 +msgid "Encapsulated Postscript (using Pango fonts)" +msgstr "" + +#: app/render_eps.c:154 +msgid "Encapsulated Postscript with preview (using Pango fonts)" +msgstr "" + +#: app/render_eps.c:164 +msgid "Encapsulated Postscript (using PostScript Latin-1 fonts)" +msgstr "" + +#: app/render_libart.c:318 lib/dialibartrenderer.c:288 +msgid "gdk_renderer: Unsupported fill mode specified!\n" +msgstr "" + +#: app/sheets.c:154 +#, c-format +msgid "" +"%s\n" +"System sheet" +msgstr "" + +#: app/sheets.c:156 +#, c-format +msgid "" +"%s\n" +"User sheet" +msgstr "" + +#: app/sheets.c:271 +msgid "" +"Can't get symbol 'custom_type' from any module.\n" +"Editing shapes is disabled." +msgstr "" + +#: app/sheets.c:373 +#, c-format +msgid "Widget not found: %s" +msgstr "" + +#: app/sheets.c:448 +msgid "SVG Shape" +msgstr "" + +#: app/sheets.c:450 +msgid "Programmed DiaObject" +msgstr "" + +#: app/sheets_dialog.c:83 +#, fuzzy +msgid "Sheets and Objects" +msgstr "ǥ ü" + +#: app/sheets_dialog.c:109 app/sheets_dialog_callbacks.c:177 +msgid "<- Copy" +msgstr "" + +#: app/sheets_dialog.c:119 app/sheets_dialog_callbacks.c:179 +msgid "<- Copy All" +msgstr "" + +#: app/sheets_dialog.c:126 app/sheets_dialog_callbacks.c:181 +#, fuzzy +msgid "<- Move" +msgstr " ̵" + +#: app/sheets_dialog.c:136 app/sheets_dialog_callbacks.c:183 +msgid "<- Move All" +msgstr "" + +#: app/sheets_dialog.c:233 +#, fuzzy +msgid "Edit" +msgstr "/(_E)" + +#: app/sheets_dialog.c:235 +#, fuzzy +msgid "_Edit" +msgstr "/(_E)" + +#: app/sheets_dialog.c:266 +#, fuzzy +msgid "Revert" +msgstr "" + +#: app/sheets_dialog.c:354 +msgid "New" +msgstr "" + +#: app/sheets_dialog.c:362 app/sheets_dialog.c:794 objects/Istar/actor.c:143 +#: objects/Istar/actor.c:144 objects/Istar/other.c:150 +#: objects/Istar/other.c:151 objects/KAOS/other.c:151 objects/KAOS/other.c:152 +#, fuzzy +msgid "Type" +msgstr ":" + +#: app/sheets_dialog.c:377 +msgid "Browse..." +msgstr "" + +#: app/sheets_dialog.c:386 +msgid "SVG Shape:" +msgstr "" + +#: app/sheets_dialog.c:413 app/sheets_dialog.c:476 +#, fuzzy +msgid "description:" +msgstr ":" + +#: app/sheets_dialog.c:425 +#, fuzzy +msgid "Sheet name:" +msgstr " ̸:" + +#: app/sheets_dialog.c:446 app/sheets_dialog_callbacks.c:289 +#: app/sheets_dialog_callbacks.c:1090 +#, fuzzy +msgid "Line Break" +msgstr " :" + +#: app/sheets_dialog.c:568 +#, fuzzy +msgid "Edit Attributes" +msgstr " Ӽ " + +#: app/sheets_dialog.c:583 +#, fuzzy +msgid "DiaObject" +msgstr "/ü(_O)" + +#: app/sheets_dialog.c:599 app/sheets_dialog.c:692 +msgid "Description:" +msgstr ":" + +#: app/sheets_dialog.c:618 objects/FS/flow-ortho.c:168 objects/FS/flow.c:146 +#: objects/UML/class_dialog.c:911 objects/UML/class_dialog.c:1845 +#: objects/UML/class_dialog.c:2074 objects/UML/class_dialog.c:2590 +#: objects/Istar/link.c:167 objects/Jackson/phenomenon.c:144 +#: objects/KAOS/metaandorrel.c:164 objects/KAOS/metabinrel.c:181 +msgid "Type:" +msgstr ":" + +#: app/sheets_dialog.c:656 +#, fuzzy +msgid "Sheet" +msgstr "" + +#: app/sheets_dialog.c:672 objects/ER/attribute.c:151 objects/ER/entity.c:133 +#: objects/ER/relationship.c:138 objects/UML/association.c:200 +#: objects/UML/association.c:1126 objects/UML/class_dialog.c:900 +#: objects/UML/class_dialog.c:1834 objects/UML/class_dialog.c:2063 +#: objects/UML/class_dialog.c:2579 objects/UML/dependency.c:133 +#: objects/UML/generalization.c:129 objects/UML/realizes.c:130 +msgid "Name:" +msgstr "̸:" + +#. Translators: Menu item Verb/Channel/Export/Remove +#. Translators: Menu item Verb/Branch/Separate/Remove +#. Translators: Menu item Verb/Branch/Separate/Remove/Remove +#: app/sheets_dialog.c:784 objects/FS/function.c:695 objects/FS/function.c:847 +#: objects/FS/function.c:849 +msgid "Remove" +msgstr "" + +#: app/sheets_dialog.c:809 +#, fuzzy +msgid "DiaObject:" +msgstr "/ü(_O)" + +#: app/sheets_dialog.c:836 +#, fuzzy +msgid "Sheet:" +msgstr "" + +#: app/sheets_dialog.c:920 +msgid "Select SVG Shape File" +msgstr "" + +#: app/sheets_dialog_callbacks.c:164 +#, fuzzy +msgid "Copy ->" +msgstr "ڿ " + +#: app/sheets_dialog_callbacks.c:166 +msgid "Copy All ->" +msgstr "" + +#: app/sheets_dialog_callbacks.c:168 +#, fuzzy +msgid "Move ->" +msgstr " ̵" + +#: app/sheets_dialog_callbacks.c:170 +msgid "Move All ->" +msgstr "" + +#: app/sheets_dialog_callbacks.c:303 +#, c-format +msgid "" +"%s\n" +"Shape" +msgstr "" + +#: app/sheets_dialog_callbacks.c:306 +#, fuzzy, c-format +msgid "" +"%s\n" +"Object" +msgstr "/ü(_O)" + +#: app/sheets_dialog_callbacks.c:309 +#, c-format +msgid "" +"%s\n" +"Unassigned type" +msgstr "" + +#: app/sheets_dialog_callbacks.c:800 +#, c-format +msgid "Filename must end with '%s': '%s'" +msgstr "" + +#: app/sheets_dialog_callbacks.c:808 +#, fuzzy, c-format +msgid "Error examining %s: %s" +msgstr "ǥ д \n" + +#: app/sheets_dialog_callbacks.c:828 +#, c-format +msgid "Could not interpret shape file: '%s'" +msgstr "" + +#: app/sheets_dialog_callbacks.c:897 +msgid "Sheet must have a Name" +msgstr "" + +#: app/sheets_dialog_callbacks.c:1594 app/sheets_dialog_callbacks.c:1600 +#, fuzzy, c-format +msgid "Couldn't open '%s': %s" +msgstr "`%s' ϴ" + +#: app/sheets_dialog_callbacks.c:1651 +#, fuzzy, c-format +msgid "Couldn't open: '%s' for writing" +msgstr " ϴ: '%s' .\n" + +#: app/sheets_dialog_callbacks.c:1660 +msgid "a user" +msgstr "" + +#: app/sheets_dialog_callbacks.c:1673 +#, fuzzy, c-format +msgid "File: %s" +msgstr "" + +#: app/sheets_dialog_callbacks.c:1676 +#, c-format +msgid "Date: %s" +msgstr "" + +#: app/sheets_dialog_callbacks.c:1680 +#, c-format +msgid "For: %s" +msgstr "" + +#: app/sheets_dialog_callbacks.c:1697 +msgid "add shapes here" +msgstr "" + +#: app/splash.c:58 +msgid "Loading ..." +msgstr "" + +#: app/splash.c:76 +#, c-format +msgid "Dia v %s" +msgstr "" + +#: dia.desktop.in.h:2 +#, fuzzy +msgid "Diagram editor" +msgstr "ǥ " + +#: lib/arrows.c:40 objects/UML/association.c:1149 objects/Jackson/domain.c:96 +msgid "None" +msgstr "" + +#: lib/arrows.c:41 +msgid "Lines" +msgstr "" + +#: lib/arrows.c:42 +msgid "Hollow Triangle" +msgstr "" + +#: lib/arrows.c:43 +msgid "Filled Triangle" +msgstr "" + +#: lib/arrows.c:44 +msgid "Unfilled Triangle" +msgstr "" + +#: lib/arrows.c:45 +#, fuzzy +msgid "Hollow Diamond" +msgstr "̸:" + +#: lib/arrows.c:46 +msgid "Filled Diamond" +msgstr "" + +#: lib/arrows.c:47 +#, fuzzy +msgid "Half Diamond" +msgstr "̸:" + +#: lib/arrows.c:48 +msgid "Half Head" +msgstr "" + +#: lib/arrows.c:49 +msgid "Slashed Cross" +msgstr "" + +#: lib/arrows.c:50 +msgid "Filled Ellipse" +msgstr "" + +#: lib/arrows.c:51 +msgid "Hollow Ellipse" +msgstr "" + +#: lib/arrows.c:52 +msgid "Filled Dot" +msgstr "" + +#: lib/arrows.c:53 +msgid "Dimension Origin" +msgstr "" + +#: lib/arrows.c:54 +msgid "Blanked Dot" +msgstr "" + +#: lib/arrows.c:55 +msgid "Double Hollow Triangle" +msgstr "" + +#: lib/arrows.c:56 +msgid "Double Filled Triangle" +msgstr "" + +#: lib/arrows.c:57 +msgid "Filled Dot and Triangle" +msgstr "" + +#: lib/arrows.c:58 +msgid "Filled Box" +msgstr "" + +#: lib/arrows.c:59 +msgid "Blanked Box" +msgstr "" + +#: lib/arrows.c:60 +#, fuzzy +msgid "Slashed" +msgstr "" + +#: lib/arrows.c:61 +msgid "Integral Symbol" +msgstr "" + +#: lib/arrows.c:62 +#, fuzzy +msgid "Crow Foot" +msgstr "۲ ũ:" + +#: lib/arrows.c:63 +#, fuzzy +msgid "Cross" +msgstr "Ŭ" + +#: lib/arrows.c:64 +msgid "1-or-many" +msgstr "" + +#: lib/arrows.c:65 +msgid "0-or-many" +msgstr "" + +#: lib/arrows.c:66 +msgid "1-or-0" +msgstr "" + +#: lib/arrows.c:67 +msgid "1 exactly" +msgstr "" + +#: lib/arrows.c:68 +#, fuzzy +msgid "Filled Concave" +msgstr " ̸:" + +#: lib/arrows.c:69 +msgid "Blanked Concave" +msgstr "" + +#: lib/arrows.c:70 +#, fuzzy +msgid "Round" +msgstr "ƴϿ" + +#: lib/arrows.c:71 +#, fuzzy +msgid "Open Round" +msgstr " ϱ" + +#: lib/arrows.c:72 +msgid "Backslash" +msgstr "" + +#: lib/bezier_conn.c:533 +msgid "Internal error: Setting corner type of endpoint of bezier" +msgstr " :  ڸ " + +#: lib/dia_xml.c:154 +#, c-format +msgid "" +"The file %s has no encoding specification;\n" +"assuming it is encoded in %s" +msgstr "" + +#: lib/dia_xml.c:471 +msgid "Taking point value of non-point node." +msgstr "" + +#: lib/dia_xml.c:482 +#, c-format +msgid "Incorrect x Point value \"%s\" %f; discarding it." +msgstr "" + +#: lib/dia_xml.c:489 +msgid "Error parsing point." +msgstr "" + +#. don't bother with useless warnings (see above) +#: lib/dia_xml.c:497 +#, c-format +msgid "Incorrect y Point value \"%s\" %f; discarding it." +msgstr "" + +#: lib/dia_xml.c:865 +msgid "" +"Your local character set is UTF-8. Because of issues with libxml1 and the " +"support of files generated by previous versions of dia, you will encounter " +"problems. Please report to dia-list@gnome.org if you see this message." +msgstr "" + +#: lib/diaarrowchooser.c:251 +msgid "Arrow Properties" +msgstr "ȭǥ Ӽ" + +#: lib/diaarrowchooser.c:333 lib/dialinechooser.c:333 +msgid "Details..." +msgstr "ڼ..." + +#. This is the default text shown in the preview entry, though the user +#. can set it. Remember that some fonts only have capital letters. +#: lib/diagtkfontsel.c:74 +msgid "abcdefghijk ABCDEFGHIJK" +msgstr "" + +#: lib/diagtkfontsel.c:209 +#, fuzzy +msgid "Font name" +msgstr " ̸:" + +#: lib/diagtkfontsel.c:210 +msgid "The X string that represents this font." +msgstr "" + +#: lib/diagtkfontsel.c:216 +#, fuzzy +msgid "Preview text" +msgstr "ڿ " + +#: lib/diagtkfontsel.c:217 +msgid "The text to display in order to demonstrate the selected font." +msgstr "" + +#: lib/diagtkfontsel.c:321 +msgid "_Family:" +msgstr "" + +#: lib/diagtkfontsel.c:327 +#, fuzzy +msgid "_Style:" +msgstr ":" + +#: lib/diagtkfontsel.c:333 +#, fuzzy +msgid "Si_ze:" +msgstr "B " + +#. create the text entry widget +#: lib/diagtkfontsel.c:462 +#, fuzzy +msgid "_Preview:" +msgstr "(_P)" + +#: lib/diagtkfontsel.c:1252 +#, fuzzy +msgid "Font Selection" +msgstr ":" + +#: lib/dialinechooser.c:297 +msgid "Line Style Properties" +msgstr " Ӽ" + +#: lib/filter.c:120 +#, c-format +msgid "Multiple export filters with unique name %s" +msgstr "" + +#: lib/font.c:89 +#, c-format +msgid "Can't load font %s.\n" +msgstr "" + +#: lib/message.c:78 lib/message.c:238 +msgid "Error" +msgstr "" + +#: lib/message.c:80 lib/message.c:226 +msgid "Warning" +msgstr "" + +#: lib/message.c:106 +msgid "There is one similar message." +msgstr "" + +#: lib/message.c:111 +#, fuzzy +msgid "Show repeated messages" +msgstr "׸ " + +#: lib/message.c:170 +#, fuzzy, c-format +msgid "There are %d similar messages." +msgstr "׸ " + +#: lib/message.c:215 +msgid "Notice" +msgstr "" + +#: lib/object_defaults.c:127 +#, fuzzy, c-format +msgid "" +"Error loading defaults '%s'.\n" +"Not a Dia diagram file." +msgstr "ǥ д \n" + +#: lib/plug-ins.c:120 +msgid "???" +msgstr "" + +#: lib/plug-ins.c:228 +#, c-format +msgid "Could not deduce correct path for `%s'" +msgstr "" + +#: lib/plug-ins.c:234 +#, fuzzy, c-format +msgid "" +"Could not load plugin '%s'\n" +"%s" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#: lib/plug-ins.c:247 +#, c-format +msgid "Could not find plugin init function in `%s'" +msgstr "÷ ʱԼ `%s' ã ϴ" + +#: lib/plug-ins.c:249 +msgid "Missing symbol 'dia_plugin_init'" +msgstr "" + +#: lib/plug-ins.c:257 lib/plug-ins.c:265 +msgid "dia_plugin_init() call failed" +msgstr "" + +#: lib/plug-ins.c:284 +#, c-format +msgid "%s Plugin could not be unloaded" +msgstr "÷ %s Ҽ ϴ" + +#: lib/plug-ins.c:372 +#, fuzzy, c-format +msgid "" +"Could not open `%s'\n" +"`%s'" +msgstr "`%s' ϴ" + +#: lib/properties.c:62 lib/properties.h:468 lib/widgets.c:791 +msgid "Left" +msgstr "" + +#: lib/properties.c:63 lib/properties.h:469 lib/widgets.c:797 +msgid "Center" +msgstr "߰" + +#: lib/properties.c:64 lib/properties.h:470 lib/widgets.c:803 +msgid "Right" +msgstr "" + +#: lib/properties.c:71 objects/chronogram/chronoline.c:179 +#: objects/chronogram/chronoref.c:158 +#, fuzzy +msgid "Line color" +msgstr " :" + +#: lib/properties.c:72 lib/properties.h:492 lib/properties.h:495 +#, fuzzy +msgid "Line style" +msgstr " :" + +#: lib/properties.c:73 +#, fuzzy +msgid "Fill color" +msgstr " :" + +#: lib/properties.c:74 lib/properties.h:505 +msgid "Draw background" +msgstr " ׸" + +#: lib/properties.c:75 lib/properties.h:509 +#, fuzzy +msgid "Start arrow" +msgstr "ȭǥ " + +#: lib/properties.c:76 lib/properties.h:512 +#, fuzzy +msgid "End arrow" +msgstr "ȭǥ " + +#: lib/properties.c:78 lib/properties.h:522 +#, fuzzy +msgid "Text alignment" +msgstr ":" + +#. all this just to make the defaults selectable ... +#: lib/properties.c:79 lib/properties.h:525 objects/GRAFCET/condition.c:132 +#: objects/GRAFCET/step.c:165 objects/GRAFCET/transition.c:142 +#: objects/UML/class.c:138 objects/UML/class_dialog.c:351 +#: objects/chronogram/chronoline.c:183 objects/chronogram/chronoref.c:164 +#, fuzzy +msgid "Font" +msgstr "۲ ũ:" + +#: lib/properties.c:80 lib/properties.h:528 objects/GRAFCET/condition.c:134 +#: objects/GRAFCET/step.c:167 objects/GRAFCET/transition.c:144 +#: objects/chronogram/chronoline.c:185 objects/chronogram/chronoref.c:166 +#, fuzzy +msgid "Font size" +msgstr "۲ ũ:" + +#: lib/properties.c:81 objects/GRAFCET/step.c:169 +#: objects/chronogram/chronoline.c:187 objects/chronogram/chronoref.c:168 +#, fuzzy +msgid "Text color" +msgstr " " + +#: lib/properties.h:486 lib/properties.h:489 +#, fuzzy +msgid "Line colour" +msgstr " :" + +#: lib/properties.h:499 lib/properties.h:502 +#, fuzzy +msgid "Fill colour" +msgstr " :" + +#: lib/properties.h:531 lib/properties.h:534 +#, fuzzy +msgid "Text colour" +msgstr " " + +#: lib/widgets.c:395 +msgid "Other fonts..." +msgstr "" + +#: lib/widgets.c:522 +#, fuzzy, c-format +msgid "Couldn't find font family for %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#. We hit the Other fonts... entry +#: lib/widgets.c:587 +#, fuzzy +msgid "Select font" +msgstr " " + +#. Translators: Menu item Noun/Material/Solid +#: lib/widgets.c:939 objects/FS/function.c:1063 +msgid "Solid" +msgstr "Ǽ" + +#: lib/widgets.c:945 +msgid "Dashed" +msgstr "" + +#: lib/widgets.c:951 +msgid "Dash-Dot" +msgstr "" + +#: lib/widgets.c:957 +msgid "Dash-Dot-Dot" +msgstr "" + +#: lib/widgets.c:963 +msgid "Dotted" +msgstr "" + +#. fs->sizebox = GTK_HBOX(box); +#: lib/widgets.c:982 +msgid "Dash length: " +msgstr "" + +#: lib/widgets.c:1137 +msgid "Select color" +msgstr " " + +#: lib/widgets.c:1355 +#, fuzzy +msgid "Size: " +msgstr "B " + +#: lib/widgets.c:1504 +msgid "Select image file" +msgstr "" + +#: lib/widgets.c:1540 +msgid "Browse" +msgstr "" + +#: objects/ER/attribute.c:153 +msgid "Key:" +msgstr "" + +#: objects/ER/attribute.c:155 +msgid "Weak key:" +msgstr "" + +#: objects/ER/attribute.c:157 +#, fuzzy +msgid "Derived:" +msgstr ":" + +#: objects/ER/attribute.c:159 +msgid "Multivalue:" +msgstr "" + +#: objects/ER/attribute.c:421 sheets/ER.sheet.in.h:1 +msgid "Attribute" +msgstr "" + +#: objects/ER/entity.c:135 +msgid "Weak:" +msgstr "" + +#: objects/ER/entity.c:137 +msgid "Associative:" +msgstr "" + +#: objects/ER/entity.c:406 objects/UML/classicon.c:126 sheets/ER.sheet.in.h:4 +msgid "Entity" +msgstr "" + +#: objects/ER/er.c:44 +msgid "Entity/Relationship diagram objects" +msgstr "" + +#: objects/ER/participation.c:115 +msgid "Total:" +msgstr "" + +#: objects/ER/participation.c:405 objects/FS/flow-ortho.c:644 +#: objects/GRAFCET/vector.c:311 objects/SADT/arrow.c:462 +#: objects/UML/association.c:752 objects/UML/component_feature.c:183 +#: objects/UML/dependency.c:372 objects/UML/generalization.c:368 +#: objects/UML/realizes.c:359 objects/standard/zigzagline.c:346 +msgid "Add segment" +msgstr "" + +#: objects/ER/participation.c:406 objects/FS/flow-ortho.c:645 +#: objects/GRAFCET/vector.c:312 objects/SADT/arrow.c:463 +#: objects/UML/association.c:753 objects/UML/component_feature.c:184 +#: objects/UML/dependency.c:373 objects/UML/generalization.c:369 +#: objects/UML/realizes.c:360 objects/standard/zigzagline.c:347 +msgid "Delete segment" +msgstr "" + +#: objects/ER/relationship.c:140 +msgid "Left Cardinality:" +msgstr "" + +#: objects/ER/relationship.c:142 +msgid "Right Cardinality:" +msgstr "" + +#: objects/ER/relationship.c:144 +#, fuzzy +msgid "Rotate:" +msgstr ":" + +#: objects/ER/relationship.c:146 +msgid "Identifying:" +msgstr "" + +#: objects/ER/relationship.c:449 sheets/ER.sheet.in.h:6 +msgid "Relationship" +msgstr "" + +#. Translators: Menu item Noun/Energy +#: objects/FS/flow-ortho.c:159 objects/FS/flow-ortho.c:641 +#: objects/FS/flow.c:137 objects/FS/flow.c:576 objects/FS/function.c:1085 +msgid "Energy" +msgstr "" + +#. Translators: Menu item Noun/Material +#: objects/FS/flow-ortho.c:160 objects/FS/flow-ortho.c:642 +#: objects/FS/flow.c:138 objects/FS/flow.c:577 objects/FS/function.c:1061 +msgid "Material" +msgstr "" + +#. Translators: Menu item Verb/Signal +#. Translators: Menu item Verb/Signal/Signal +#. Translators: Menu item Noun/Signal +#. Translators: Menu item Noun/Signal/Signal +#: objects/FS/flow-ortho.c:161 objects/FS/flow-ortho.c:643 +#: objects/FS/flow.c:139 objects/FS/flow.c:578 objects/FS/function.c:1023 +#: objects/FS/function.c:1025 objects/FS/function.c:1161 +#: objects/FS/function.c:1163 +msgid "Signal" +msgstr "" + +#: objects/FS/fs.c:43 +msgid "Function structure diagram objects" +msgstr "" + +#: objects/FS/function.c:140 +msgid "Wish function" +msgstr "" + +#: objects/FS/function.c:142 +msgid "User function" +msgstr "" + +#. Translators: Menu item Verb +#: objects/FS/function.c:665 +msgid "Verb" +msgstr "" + +#. Translators: Menu item Verb/Channel +#. Translators: Menu item Verb/Channel/Channel +#. Translators: Menu item Verb/Channel/Transfer/Transport/Channel +#: objects/FS/function.c:667 objects/FS/function.c:669 +#: objects/FS/function.c:709 +msgid "Channel" +msgstr "" + +#. Translators: Menu item Verb/Channel/Import +#. Translators: Menu item Verb/Channel/Import/Import +#: objects/FS/function.c:671 objects/FS/function.c:673 +msgid "Import" +msgstr "" + +#. Translators: Menu item Verb/Channel/Import/Input +#: objects/FS/function.c:675 objects/KAOS/metabinrel.c:173 +msgid "Input" +msgstr "" + +#. Translators: Menu item Verb/Channel/Import/Receive +#: objects/FS/function.c:677 +#, fuzzy +msgid "Receive" +msgstr "" + +#. Translators: Menu item Verb/Channel/Import/Allow +#. Translators: Menu item Verb/Control Magnitude/Regulate/Allow +#: objects/FS/function.c:679 objects/FS/function.c:951 +msgid "Allow" +msgstr "" + +#. Translators: Menu item Verb/Channel/Import/Form Entrance +#: objects/FS/function.c:681 +msgid "Form Entrance" +msgstr "" + +#. Translators: Menu item Verb/Channel/Import/Capture +#. Translators: Menu item Verb/Provision/Store/Capture +#: objects/FS/function.c:683 objects/FS/function.c:917 +#, fuzzy +msgid "Capture" +msgstr "" + +#. Translators: Menu item Verb/Channel/Export/Discharge +#: objects/FS/function.c:689 +msgid "Discharge" +msgstr "" + +#. Translators: Menu item Verb/Channel/Export/Eject +#: objects/FS/function.c:691 +#, fuzzy +msgid "Eject" +msgstr "ü" + +#. Translators: Menu item Verb/Channel/Export/Dispose +#: objects/FS/function.c:693 +msgid "Dispose" +msgstr "" + +#. Translators: Menu item Verb/Channel/Transfer +#. Translators: Menu item Verb/Channel/Transfer/Transfer +#. Translators: Menu item Verb/Channel/Transfer/Transmit/Transfer +#: objects/FS/function.c:697 objects/FS/function.c:699 +#: objects/FS/function.c:717 +msgid "Transfer" +msgstr "" + +#. Translators: Menu item Verb/Channel/Transfer/Transport +#. Translators: Menu item Verb/Channel/Transfer/Transport/Transport +#: objects/FS/function.c:701 objects/FS/function.c:703 +msgid "Transport" +msgstr "" + +#. Translators: Menu item Verb/Channel/Transfer/Transport/Lift +#: objects/FS/function.c:705 +#, fuzzy +msgid "Lift" +msgstr "" + +#. Translators: Menu item Verb/Channel/Transfer/Transport/Move +#: objects/FS/function.c:707 +#, fuzzy +msgid "Move" +msgstr " ̵" + +#. Translators: Menu item Verb/Channel/Transfer/Transmit +#. Translators: Menu item Verb/Channel/Transfer/Transmit/Transmit +#: objects/FS/function.c:711 objects/FS/function.c:713 +msgid "Transmit" +msgstr "" + +#. Translators: Menu item Verb/Channel/Transfer/Transmit/Conduct +#: objects/FS/function.c:715 +msgid "Conduct" +msgstr "" + +#. Translators: Menu item Verb/Channel/Transfer/Transmit/Convey +#: objects/FS/function.c:719 +msgid "Convey" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide +#. Translators: Menu item Verb/Channel/Guide/Guide +#. Translators: Menu item Verb/Channel/Guide/Guide/Guide +#: objects/FS/function.c:721 objects/FS/function.c:723 +#: objects/FS/function.c:725 +#, fuzzy +msgid "Guide" +msgstr ":" + +#. Translators: Menu item Verb/Channel/Guide/Guide/Direct +#: objects/FS/function.c:727 +msgid "Direct" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Guide/Straighten +#: objects/FS/function.c:729 +msgid "Straighten" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Guide/Steer +#: objects/FS/function.c:731 +msgid "Steer" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Translate +#: objects/FS/function.c:733 +msgid "Translate" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Rotate +#. Translators: Menu item Verb/Channel/Guide/Rotate/Rotate +#: objects/FS/function.c:735 objects/FS/function.c:737 +#, fuzzy +msgid "Rotate" +msgstr ":" + +#. Translators: Menu item Verb/Channel/Guide/Rotate/Turn +#: objects/FS/function.c:739 +msgid "Turn" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Rotate/Spin +#: objects/FS/function.c:741 +#, fuzzy +msgid "Spin" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Allow DOF +#. Translators: Menu item Verb/Channel/Guide/Allow DOF/Allow DOF +#: objects/FS/function.c:743 objects/FS/function.c:745 +msgid "Allow DOF" +msgstr "" + +#. Translators: Menu item Verb/Channel/Guide/Allow DOF/Constrain +#: objects/FS/function.c:747 +#, fuzzy +msgid "Constrain" +msgstr "ռ" + +#. Translators: Menu item Verb/Channel/Guide/Allow DOF/Unlock +#: objects/FS/function.c:749 +#, fuzzy +msgid "Unlock" +msgstr "ϱ" + +#. Translators: Menu item Verb/Support +#. Translators: Menu item Verb/Support/Support +#: objects/FS/function.c:751 objects/FS/function.c:753 +msgid "Support" +msgstr "" + +#. Translators: Menu item Verb/Support/Stop +#. Translators: Menu item Verb/Support/Stop/Stop +#: objects/FS/function.c:755 objects/FS/function.c:757 +#: sheets/SDL.sheet.in.h:19 +msgid "Stop" +msgstr "" + +#. Translators: Menu item Verb/Support/Stop/Insulate +#: objects/FS/function.c:759 +msgid "Insulate" +msgstr "" + +#. Translators: Menu item Verb/Support/Stop/Protect +#: objects/FS/function.c:761 +msgid "Protect" +msgstr "" + +#. Translators: Menu item Verb/Support/Stop/Prevent +#. Translators: Menu item Verb/Control Magnitude/Regulate/Prevent +#: objects/FS/function.c:763 objects/FS/function.c:953 +msgid "Prevent" +msgstr "" + +#. Translators: Menu item Verb/Support/Stop/Shield +#: objects/FS/function.c:765 +msgid "Shield" +msgstr "" + +#. Translators: Menu item Verb/Support/Stop/Inhibit +#: objects/FS/function.c:767 +msgid "Inhibit" +msgstr "" + +#. Translators: Menu item Verb/Support/Stabilize +#. Translators: Menu item Verb/Support/Stabilize/Stabilize +#: objects/FS/function.c:769 objects/FS/function.c:771 +msgid "Stabilize" +msgstr "" + +#. Translators: Menu item Verb/Support/Stabilize/Steady +#: objects/FS/function.c:773 +msgid "Steady" +msgstr "" + +#. Translators: Menu item Verb/Support/Secure +#. Translators: Menu item Verb/Support/Secure/Secure +#: objects/FS/function.c:775 objects/FS/function.c:777 +msgid "Secure" +msgstr "" + +#. Translators: Menu item Verb/Support/Secure/Attach +#. Translators: Menu item Verb/Connect/Couple/Attach +#: objects/FS/function.c:779 objects/FS/function.c:811 +msgid "Attach" +msgstr "" + +#. Translators: Menu item Verb/Support/Secure/Mount +#: objects/FS/function.c:781 +#, fuzzy +msgid "Mount" +msgstr "۲ ũ:" + +#. Translators: Menu item Verb/Support/Secure/Lock +#: objects/FS/function.c:783 sheets/ciscomisc.sheet.in.h:21 +msgid "Lock" +msgstr "" + +#. Translators: Menu item Verb/Support/Secure/Fasten +#: objects/FS/function.c:785 +msgid "Fasten" +msgstr "" + +#. Translators: Menu item Verb/Support/Secure/Hold +#: objects/FS/function.c:787 +#, fuzzy +msgid "Hold" +msgstr "Ǽ" + +#. Translators: Menu item Verb/Support/Position +#. Translators: Menu item Verb/Support/Position/Position +#: objects/FS/function.c:789 objects/FS/function.c:791 +#: objects/Istar/actor.c:68 +#, fuzzy +msgid "Position" +msgstr "ռ" + +#. Translators: Menu item Verb/Support/Position/Orient +#: objects/FS/function.c:793 +#, fuzzy +msgid "Orient" +msgstr "" + +#. Translators: Menu item Verb/Support/Position/Align +#: objects/FS/function.c:795 +#, fuzzy +msgid "Align" +msgstr ":" + +#. Translators: Menu item Verb/Support/Position/Locate +#. Translators: Menu item Verb/Signal/Sense/Locate +#: objects/FS/function.c:797 objects/FS/function.c:1039 +#, fuzzy +msgid "Locate" +msgstr "÷:" + +#. Translators: Menu item Verb/Connect +#. Translators: Menu item Verb/Connect/Connect +#: objects/FS/function.c:799 objects/FS/function.c:801 +msgid "Connect" +msgstr "" + +#. Translators: Menu item Verb/Connect/Couple +#. Translators: Menu item Verb/Connect/Couple/Couple +#: objects/FS/function.c:803 objects/FS/function.c:805 +msgid "Couple" +msgstr "" + +#. Translators: Menu item Verb/Connect/Couple/Join +#: objects/FS/function.c:807 +msgid "Join" +msgstr "" + +#. Translators: Menu item Verb/Connect/Couple/Assemble +#: objects/FS/function.c:809 +msgid "Assemble" +msgstr "" + +#. Translators: Menu item Verb/Connect/Mix +#. Translators: Menu item Verb/Connect/Mix/Mix +#: objects/FS/function.c:813 objects/FS/function.c:815 +msgid "Mix" +msgstr "" + +#. Translators: Menu item Verb/Connect/Mix/Combine +#: objects/FS/function.c:817 +msgid "Combine" +msgstr "" + +#. Translators: Menu item Verb/Connect/Mix/Blend +#: objects/FS/function.c:819 +#, fuzzy +msgid "Blend" +msgstr "" + +#. Translators: Menu item Verb/Connect/Mix/Add +#: objects/FS/function.c:821 +msgid "Add" +msgstr "" + +#. Translators: Menu item Verb/Connect/Mix/Pack +#: objects/FS/function.c:823 +msgid "Pack" +msgstr "" + +#. Translators: Menu item Verb/Connect/Mix/Coalesce +#: objects/FS/function.c:825 +#, fuzzy +msgid "Coalesce" +msgstr "ݱ" + +#. Translators: Menu item Verb/Branch +#. Translators: Menu item Verb/Branch/Branch +#: objects/FS/function.c:827 objects/FS/function.c:829 sheets/UML.sheet.in.h:5 +msgid "Branch" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate +#. Translators: Menu item Verb/Branch/Separate/Separate +#. Translators: Menu item Verb/Branch/Separate/Separate/Separate +#: objects/FS/function.c:831 objects/FS/function.c:833 +#: objects/FS/function.c:835 +#, fuzzy +msgid "Separate" +msgstr " :" + +#. Translators: Menu item Verb/Branch/Separate/Separate/Switch +#: objects/FS/function.c:837 +msgid "Switch" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Separate/Divide +#: objects/FS/function.c:839 +msgid "Divide" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Separate/Release +#: objects/FS/function.c:841 +#, fuzzy +msgid "Release" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Separate/Detach +#: objects/FS/function.c:843 +msgid "Detach" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Separate/Disconnect +#: objects/FS/function.c:845 +msgid "Disconnect" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Remove/Cut +#: objects/FS/function.c:851 +msgid "Cut" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Remove/Polish +#: objects/FS/function.c:853 +msgid "Polish" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Remove/Sand +#: objects/FS/function.c:855 +msgid "Sand" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Remove/Drill +#: objects/FS/function.c:857 +msgid "Drill" +msgstr "" + +#. Translators: Menu item Verb/Branch/Separate/Remove/Lathe +#: objects/FS/function.c:859 +msgid "Lathe" +msgstr "" + +#. Translators: Menu item Verb/Branch/Refine +#. Translators: Menu item Verb/Branch/Refine/Refine +#: objects/FS/function.c:861 objects/FS/function.c:863 +#, fuzzy +msgid "Refine" +msgstr "" + +#. Translators: Menu item Verb/Branch/Refine/Purify +#: objects/FS/function.c:865 +msgid "Purify" +msgstr "" + +#. Translators: Menu item Verb/Branch/Refine/Strain +#: objects/FS/function.c:867 +#, fuzzy +msgid "Strain" +msgstr "" + +#. Translators: Menu item Verb/Branch/Refine/Filter +#: objects/FS/function.c:869 +#, fuzzy +msgid "Filter" +msgstr "" + +#. Translators: Menu item Verb/Branch/Refine/Percolate +#: objects/FS/function.c:871 +msgid "Percolate" +msgstr "" + +#. Translators: Menu item Verb/Branch/Refine/Clear +#: objects/FS/function.c:873 +#, fuzzy +msgid "Clear" +msgstr "Ŭ" + +#. Translators: Menu item Verb/Branch/Distribute +#. Translators: Menu item Verb/Branch/Distribute/Distribute +#: objects/FS/function.c:875 objects/FS/function.c:877 +#, fuzzy +msgid "Distribute" +msgstr "Ӽ" + +#. Translators: Menu item Verb/Branch/Distribute/Diverge +#: objects/FS/function.c:879 +msgid "Diverge" +msgstr "" + +#. Translators: Menu item Verb/Branch/Distribute/Scatter +#: objects/FS/function.c:881 +#, fuzzy +msgid "Scatter" +msgstr ":" + +#. Translators: Menu item Verb/Branch/Distribute/Disperse +#: objects/FS/function.c:883 +msgid "Disperse" +msgstr "" + +#. Translators: Menu item Verb/Branch/Distribute/Diffuse +#. Translators: Menu item Verb/Branch/Dissipate/Diffuse +#: objects/FS/function.c:885 objects/FS/function.c:899 +msgid "Diffuse" +msgstr "" + +#. Translators: Menu item Verb/Branch/Distribute/Empty +#: objects/FS/function.c:887 +msgid "Empty" +msgstr "" + +#. Translators: Menu item Verb/Branch/Dissipate +#. Translators: Menu item Verb/Branch/Dissipate/Dissipate +#: objects/FS/function.c:889 objects/FS/function.c:891 +msgid "Dissipate" +msgstr "" + +#. Translators: Menu item Verb/Branch/Dissipate/Absorb +#: objects/FS/function.c:893 +msgid "Absorb" +msgstr "" + +#. Translators: Menu item Verb/Branch/Dissipate/Dampen +#: objects/FS/function.c:895 +#, fuzzy +msgid "Dampen" +msgstr "̸:" + +#. Translators: Menu item Verb/Branch/Dissipate/Dispel +#: objects/FS/function.c:897 +msgid "Dispel" +msgstr "" + +#. Translators: Menu item Verb/Branch/Dissipate/Resist +#: objects/FS/function.c:901 +msgid "Resist" +msgstr "" + +#. Translators: Menu item Verb/Provision +#. Translators: Menu item Verb/Provision/Provision +#: objects/FS/function.c:903 objects/FS/function.c:905 +msgid "Provision" +msgstr "" + +#. Translators: Menu item Verb/Provision/Store +#. Translators: Menu item Verb/Provision/Store/Store +#: objects/FS/function.c:907 objects/FS/function.c:909 +msgid "Store" +msgstr "" + +#. Translators: Menu item Verb/Provision/Store/Contain +#: objects/FS/function.c:911 +msgid "Contain" +msgstr "" + +#. Translators: Menu item Verb/Provision/Store/Collect +#: objects/FS/function.c:913 +msgid "Collect" +msgstr "" + +#. Translators: Menu item Verb/Provision/Store/Reserve +#: objects/FS/function.c:915 +#, fuzzy +msgid "Reserve" +msgstr "" + +#. Translators: Menu item Verb/Provision/Supply +#. Translators: Menu item Verb/Provision/Supply/Supply +#: objects/FS/function.c:919 objects/FS/function.c:921 +#, fuzzy +msgid "Supply" +msgstr "" + +#. Translators: Menu item Verb/Provision/Supply/Fill +#: objects/FS/function.c:923 +#, fuzzy +msgid "Fill" +msgstr "" + +#. Translators: Menu item Verb/Provision/Supply/Provide +#: objects/FS/function.c:925 +msgid "Provide" +msgstr "" + +#. Translators: Menu item Verb/Provision/Supply/Replenish +#: objects/FS/function.c:927 +msgid "Replenish" +msgstr "" + +#. Translators: Menu item Verb/Provision/Supply/Expose +#: objects/FS/function.c:929 +#, fuzzy +msgid "Expose" +msgstr "Ÿ" + +#. Translators: Menu item Verb/Provision/Extract +#: objects/FS/function.c:931 sheets/Flowchart.sheet.in.h:7 +#, fuzzy +msgid "Extract" +msgstr "߻" + +#. Translators: Menu item Verb/Control Magnitude +#. Translators: Menu item Verb/Control Magnitude/Control Magnitude +#: objects/FS/function.c:933 objects/FS/function.c:935 +msgid "Control Magnitude" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Actuate +#. Translators: Menu item Verb/Control Magnitude/Actuate/Actuate +#: objects/FS/function.c:937 objects/FS/function.c:939 +msgid "Actuate" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Actuate/Start +#: objects/FS/function.c:941 +#, fuzzy +msgid "Start" +msgstr "ȭǥ " + +#. Translators: Menu item Verb/Control Magnitude/Actuate/Initiate +#: objects/FS/function.c:943 +msgid "Initiate" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Regulate +#. Translators: Menu item Verb/Control Magnitude/Regulate/Regulate +#: objects/FS/function.c:945 objects/FS/function.c:947 +#, fuzzy +msgid "Regulate" +msgstr "ø" + +#. Translators: Menu item Verb/Control Magnitude/Regulate/Control +#. Translators: Menu item Noun/Signal/Control +#: objects/FS/function.c:949 objects/FS/function.c:1167 +#: objects/UML/classicon.c:124 +msgid "Control" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Regulate/Enable +#: objects/FS/function.c:955 +msgid "Enable" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Regulate/Disable +#: objects/FS/function.c:957 +msgid "Disable" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Regulate/Limit +#: objects/FS/function.c:959 +msgid "Limit" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Regulate/Interrupt +#: objects/FS/function.c:961 +msgid "Interrupt" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change +#. Translators: Menu item Verb/Control Magnitude/Change/Change +#: objects/FS/function.c:963 objects/FS/function.c:965 +#, fuzzy +msgid "Change" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Increase +#: objects/FS/function.c:967 +msgid "Increase" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Decrease +#: objects/FS/function.c:969 +msgid "Decrease" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Amplify +#: objects/FS/function.c:971 +#, fuzzy +msgid "Amplify" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Reduce +#: objects/FS/function.c:973 +msgid "Reduce" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Normalize +#: objects/FS/function.c:977 +msgid "Normalize" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Multiply +#: objects/FS/function.c:979 +#, fuzzy +msgid "Multiply" +msgstr "ߺ" + +#. Translators: Menu item Verb/Control Magnitude/Change/Scale +#: objects/FS/function.c:981 +#, fuzzy +msgid "Scale" +msgstr ":" + +#. Translators: Menu item Verb/Control Magnitude/Change/Rectify +#: objects/FS/function.c:983 +msgid "Rectify" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Change/Adjust +#: objects/FS/function.c:985 +msgid "Adjust" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Form +#. Translators: Menu item Verb/Control Magnitude/Form/Form +#: objects/FS/function.c:987 objects/FS/function.c:989 +msgid "Form" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Form/Compact +#: objects/FS/function.c:991 +msgid "Compact" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Form/Crush +#: objects/FS/function.c:993 +msgid "Crush" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Form/Shape +#: objects/FS/function.c:995 +msgid "Shape" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Form/Compress +#: objects/FS/function.c:997 +msgid "Compress" +msgstr "" + +#. Translators: Menu item Verb/Control Magnitude/Form/Pierce +#: objects/FS/function.c:999 +#, fuzzy +msgid "Pierce" +msgstr "" + +#. Translators: Menu item Verb/Convert +#. Translators: Menu item Verb/Convert/Convert +#: objects/FS/function.c:1001 objects/FS/function.c:1003 +msgid "Convert" +msgstr "" + +#. Translators: Menu item Verb/Convert/Transform +#: objects/FS/function.c:1005 +msgid "Transform" +msgstr "" + +#. Translators: Menu item Verb/Convert/Liquefy +#: objects/FS/function.c:1007 +msgid "Liquefy" +msgstr "" + +#. Translators: Menu item Verb/Convert/Solidify +#: objects/FS/function.c:1009 +#, fuzzy +msgid "Solidify" +msgstr "Ǽ" + +#. Translators: Menu item Verb/Convert/Evaporate +#: objects/FS/function.c:1011 +msgid "Evaporate" +msgstr "" + +#. Translators: Menu item Verb/Convert/Sublimate +#: objects/FS/function.c:1013 +msgid "Sublimate" +msgstr "" + +#. Translators: Menu item Verb/Convert/Condense +#: objects/FS/function.c:1015 +#, fuzzy +msgid "Condense" +msgstr "ݱ" + +#. Translators: Menu item Verb/Convert/Integrate +#: objects/FS/function.c:1017 +#, fuzzy +msgid "Integrate" +msgstr " ̽" + +#. Translators: Menu item Verb/Convert/Differentiate +#: objects/FS/function.c:1019 +msgid "Differentiate" +msgstr "" + +#. Translators: Menu item Verb/Convert/Process +#: objects/FS/function.c:1021 sheets/EML.sheet.in.h:5 +msgid "Process" +msgstr "" + +#. Translators: Menu item Verb/Signal/Sense +#. Translators: Menu item Verb/Signal/Sense/Sense +#: objects/FS/function.c:1027 objects/FS/function.c:1029 +msgid "Sense" +msgstr "" + +#. Translators: Menu item Verb/Signal/Sense/Perceive +#: objects/FS/function.c:1031 +#, fuzzy +msgid "Perceive" +msgstr ":" + +#. Translators: Menu item Verb/Signal/Sense/Recognize +#: objects/FS/function.c:1033 +msgid "Recognize" +msgstr "" + +#. Translators: Menu item Verb/Signal/Sense/Discern +#: objects/FS/function.c:1035 +msgid "Discern" +msgstr "" + +#. Translators: Menu item Verb/Signal/Sense/Check +#: objects/FS/function.c:1037 +msgid "Check" +msgstr "" + +#. Translators: Menu item Verb/Signal/Sense/Verify +#: objects/FS/function.c:1041 +msgid "Verify" +msgstr "" + +#. Translators: Menu item Verb/Signal/Indicate +#. Translators: Menu item Verb/Signal/Indicate/Indicate +#: objects/FS/function.c:1043 objects/FS/function.c:1045 +msgid "Indicate" +msgstr "" + +#. Translators: Menu item Verb/Signal/Indicate/Mark +#: objects/FS/function.c:1047 +msgid "Mark" +msgstr "" + +#. Translators: Menu item Verb/Signal/Display +#: objects/FS/function.c:1049 sheets/Flowchart.sheet.in.h:5 +msgid "Display" +msgstr "" + +#. Translators: Menu item Verb/Signal/Measure +#. Translators: Menu item Verb/Signal/Measure/Measure +#: objects/FS/function.c:1051 objects/FS/function.c:1053 +msgid "Measure" +msgstr "" + +#. Translators: Menu item Verb/Signal/Measure/Calculate +#: objects/FS/function.c:1055 +#, fuzzy +msgid "Calculate" +msgstr "ȣ" + +#. Translators: Menu item Verb/Signal/Represent +#: objects/FS/function.c:1057 +msgid "Represent" +msgstr "" + +#. Translators: Menu item Noun +#: objects/FS/function.c:1059 +#, fuzzy +msgid "Noun" +msgstr "ƴϿ" + +#. Translators: Menu item Noun/Material/Liquid +#: objects/FS/function.c:1065 +msgid "Liquid" +msgstr "" + +#. Translators: Menu item Noun/Material/Gas +#: objects/FS/function.c:1067 +msgid "Gas" +msgstr "" + +#. Translators: Menu item Noun/Material/Human +#. Translators: Menu item Noun/Material/Human/Human +#. Translators: Menu item Noun/Energy/Human +#: objects/FS/function.c:1069 objects/FS/function.c:1071 +#: objects/FS/function.c:1155 +msgid "Human" +msgstr "" + +#. Translators: Menu item Noun/Material/Human/Hand +#: objects/FS/function.c:1073 +msgid "Hand" +msgstr "" + +#. Translators: Menu item Noun/Material/Human/Foot +#: objects/FS/function.c:1075 +#, fuzzy +msgid "Foot" +msgstr "۲ ũ:" + +#. Translators: Menu item Noun/Material/Human/Head +#: objects/FS/function.c:1077 +msgid "Head" +msgstr "" + +#. Translators: Menu item Noun/Material/Human/Finger +#: objects/FS/function.c:1079 +#, fuzzy +msgid "Finger" +msgstr "" + +#. Translators: Menu item Noun/Material/Human/Toe +#: objects/FS/function.c:1081 +#, fuzzy +msgid "Toe" +msgstr "" + +#. Translators: Menu item Noun/Material/Biological +#: objects/FS/function.c:1083 +msgid "Biological" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical +#: objects/FS/function.c:1087 +msgid "Mechanical" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Mech. Energy +#: objects/FS/function.c:1089 +msgid "Mech. Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Translation +#: objects/FS/function.c:1091 +#, fuzzy +msgid "Translation" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Force +#: objects/FS/function.c:1093 +msgid "Force" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Rotation +#: objects/FS/function.c:1095 +#, fuzzy +msgid "Rotation" +msgstr ":" + +#. Translators: Menu item Noun/Energy/Mechanical/Torque +#: objects/FS/function.c:1097 +msgid "Torque" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Random Motion +#: objects/FS/function.c:1099 +msgid "Random Motion" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Vibration +#: objects/FS/function.c:1101 +#, fuzzy +msgid "Vibration" +msgstr "۵" + +#. Translators: Menu item Noun/Energy/Mechanical/Rotational Energy +#: objects/FS/function.c:1103 +msgid "Rotational Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Mechanical/Translational Energy +#: objects/FS/function.c:1105 +msgid "Translational Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Electricity +#: objects/FS/function.c:1107 +#, fuzzy +msgid "Electrical" +msgstr "¿ ٲ" + +#. Translators: Menu item Noun/Energy/Electricity/Electricity +#: objects/FS/function.c:1109 +msgid "Electricity" +msgstr "" + +#. Translators: Menu item Noun/Energy/Electricity/Voltage +#: objects/FS/function.c:1111 +msgid "Voltage" +msgstr "" + +#. Translators: Menu item Noun/Energy/Electricity/Current +#: objects/FS/function.c:1113 +#, fuzzy +msgid "Current" +msgstr "߰" + +#. Translators: Menu item Noun/Energy/Hydraulic +#: objects/FS/function.c:1115 +msgid "Hydraulic" +msgstr "" + +#. Translators: Menu item Noun/Energy/Hydraulic/Pressure +#: objects/FS/function.c:1117 +msgid "Pressure" +msgstr "" + +#. Translators: Menu item Noun/Energy/Hydraulic/Volumetric Flow +#: objects/FS/function.c:1119 +msgid "Volumetric Flow" +msgstr "" + +#. Translators: Menu item Noun/Energy/Thermal +#: objects/FS/function.c:1121 +msgid "Thermal" +msgstr "" + +#. Translators: Menu item Noun/Energy/Thermal/Heat +#: objects/FS/function.c:1123 +#, fuzzy +msgid "Heat" +msgstr "" + +#. Translators: Menu item Noun/Energy/Thermal/Conduction +#: objects/FS/function.c:1125 +msgid "Conduction" +msgstr "" + +#. Translators: Menu item Noun/Energy/Thermal/Convection +#: objects/FS/function.c:1127 +#, fuzzy +msgid "Convection" +msgstr ":" + +#. Translators: Menu item Noun/Energy/Thermal/Radiation +#. Translators: Menu item Noun/Energy/Radioactive/Radiation +#: objects/FS/function.c:1129 objects/FS/function.c:1137 +#, fuzzy +msgid "Radiation" +msgstr "" + +#. Translators: Menu item Noun/Energy/Pneumatic +#: objects/FS/function.c:1131 +msgid "Pneumatic" +msgstr "" + +#. Translators: Menu item Noun/Energy/Chemical +#: objects/FS/function.c:1133 +msgid "Chemical" +msgstr "" + +#. Translators: Menu item Noun/Energy/Radioactive +#: objects/FS/function.c:1135 +msgid "Radioactive" +msgstr "" + +#. Translators: Menu item Noun/Energy/Radioactive/Microwaves +#: objects/FS/function.c:1139 +msgid "Microwaves" +msgstr "" + +#. Translators: Menu item Noun/Energy/Radioactive/Radio waves +#: objects/FS/function.c:1141 +msgid "Radio waves" +msgstr "" + +#. Translators: Menu item Noun/Energy/Radioactive/X-Rays +#: objects/FS/function.c:1143 +msgid "X-Rays" +msgstr "" + +#. Translators: Menu item Noun/Energy/Radioactive/Gamma Rays +#: objects/FS/function.c:1145 +msgid "Gamma Rays" +msgstr "" + +#. Translators: Menu item Noun/Energy/Acoustic Energy +#: objects/FS/function.c:1147 +msgid "Acoustic Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Optical Energy +#: objects/FS/function.c:1149 +msgid "Optical Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Solar Energy +#: objects/FS/function.c:1151 +msgid "Solar Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Magnetic Energy +#: objects/FS/function.c:1153 +msgid "Magnetic Energy" +msgstr "" + +#. Translators: Menu item Noun/Energy/Human/Human Motion +#: objects/FS/function.c:1157 +msgid "Human Motion" +msgstr "" + +#. Translators: Menu item Noun/Energy/Human/Human Force +#: objects/FS/function.c:1159 +msgid "Human Force" +msgstr "" + +#. Translators: Menu item Noun/Signal/Status +#: objects/FS/function.c:1165 +msgid "Status" +msgstr "" + +#: objects/FS/function.c:1169 +msgid "User/Device Fn" +msgstr "" + +#: objects/FS/function.c:1170 +msgid "Wish Fn" +msgstr "" + +#: objects/GRAFCET/action.c:135 sheets/SDL.sheet.in.h:9 +msgid "Macro call" +msgstr "" + +#: objects/GRAFCET/action.c:135 +msgid "This action is a call to a macro-step" +msgstr "" + +#: objects/GRAFCET/condition.c:130 +#, fuzzy +msgid "Condition" +msgstr "ռ" + +#: objects/GRAFCET/condition.c:130 +msgid "The boolean equation of the condition" +msgstr "" + +#: objects/GRAFCET/condition.c:132 +msgid "The condition's font" +msgstr "" + +#: objects/GRAFCET/condition.c:134 +msgid "The condition's font size" +msgstr "" + +#: objects/GRAFCET/condition.c:137 objects/GRAFCET/transition.c:147 +#, fuzzy +msgid "Color" +msgstr "ݱ" + +#: objects/GRAFCET/condition.c:137 +msgid "The condition's color" +msgstr "" + +#: objects/GRAFCET/grafcet.c:45 +msgid "GRAFCET diagram objects" +msgstr "" + +#: objects/GRAFCET/step.c:146 sheets/GRAFCET.sheet.in.h:13 +msgid "Regular step" +msgstr "" + +#: objects/GRAFCET/step.c:147 sheets/GRAFCET.sheet.in.h:6 +msgid "Initial step" +msgstr "" + +#: objects/GRAFCET/step.c:148 sheets/GRAFCET.sheet.in.h:8 +msgid "Macro entry step" +msgstr "" + +#: objects/GRAFCET/step.c:149 sheets/GRAFCET.sheet.in.h:9 +msgid "Macro exit step" +msgstr "" + +#: objects/GRAFCET/step.c:150 sheets/GRAFCET.sheet.in.h:7 +msgid "Macro call step" +msgstr "" + +#: objects/GRAFCET/step.c:151 +msgid "Subprogram call step" +msgstr "" + +#: objects/GRAFCET/step.c:158 +msgid "Step name" +msgstr "" + +#: objects/GRAFCET/step.c:158 +msgid "The name of the step" +msgstr "" + +#: objects/GRAFCET/step.c:161 +msgid "Step type" +msgstr "" + +#: objects/GRAFCET/step.c:161 +msgid "The kind of step" +msgstr "" + +#: objects/GRAFCET/step.c:163 +msgid "Active" +msgstr "" + +#: objects/GRAFCET/step.c:163 +msgid "Shows a red dot to figure the step's activity" +msgstr "" + +#: objects/GRAFCET/transition.c:140 +msgid "Receptivity" +msgstr "" + +#: objects/GRAFCET/transition.c:140 +msgid "The boolean equation of the receptivity" +msgstr "" + +#: objects/GRAFCET/transition.c:142 +msgid "The receptivity's font" +msgstr "" + +#: objects/GRAFCET/transition.c:144 +msgid "The receptivity's font size" +msgstr "" + +#: objects/GRAFCET/transition.c:147 +msgid "The receptivity's color" +msgstr "" + +#: objects/GRAFCET/transition.c:148 +msgid "North point" +msgstr "" + +#: objects/GRAFCET/transition.c:149 +#, fuzzy +msgid "South point" +msgstr "ε巯 " + +#: objects/GRAFCET/vector.c:128 +msgid "Draw arrow heads on upward arcs:" +msgstr "" + +#: objects/GRAFCET/vergent.c:122 +msgid "OR" +msgstr "" + +#: objects/GRAFCET/vergent.c:123 +msgid "AND" +msgstr "" + +#: objects/GRAFCET/vergent.c:134 +msgid "Vergent type:" +msgstr "" + +#: objects/GRAFCET/vergent.c:405 objects/SADT/box.c:445 +#: objects/Istar/other.c:483 objects/Jackson/domain.c:539 +#: objects/KAOS/goal.c:587 objects/KAOS/other.c:535 +#: objects/standard/line.c:232 +msgid "Add connection point" +msgstr "" + +#: objects/GRAFCET/vergent.c:406 +msgid "Delete connection point" +msgstr "" + +#: objects/GRAFCET/vergent.c:410 +msgid "GRAFCET OR/AND vergent" +msgstr "" + +#: objects/Misc/analog_clock.c:136 +#, fuzzy +msgid "Arrow color" +msgstr " :" + +#: objects/Misc/analog_clock.c:138 +#, fuzzy +msgid "Arrow line width" +msgstr " " + +#: objects/Misc/analog_clock.c:140 +#, fuzzy +msgid "Seconds arrow color" +msgstr " " + +#: objects/Misc/analog_clock.c:142 +#, fuzzy +msgid "Seconds arrow line width" +msgstr " " + +#: objects/Misc/analog_clock.c:144 +#, fuzzy +msgid "Show hours" +msgstr " (_R)" + +#: objects/Misc/libmisc.c:38 +msgid "Miscellaneous objects" +msgstr "" + +#. property rows +#: objects/SADT/arrow.c:134 objects/UML/class.c:143 +#: objects/UML/class_dialog.c:357 +msgid "Normal" +msgstr "" + +#: objects/SADT/arrow.c:135 +msgid "Import resource (not shown upstairs)" +msgstr "" + +#: objects/SADT/arrow.c:136 +msgid "Imply resource (not shown downstairs)" +msgstr "" + +#: objects/SADT/arrow.c:137 +msgid "Dotted arrow" +msgstr "" + +#: objects/SADT/arrow.c:138 +msgid "disable arrow heads" +msgstr "" + +#: objects/SADT/arrow.c:144 +msgid "Flow style:" +msgstr "" + +#: objects/SADT/arrow.c:146 +msgid "Automatically gray vertical flows:" +msgstr "" + +#: objects/SADT/arrow.c:147 +msgid "" +"To improve the ease of reading, flows which begin and end vertically can be " +"rendered gray" +msgstr "" + +#: objects/SADT/arrow.c:468 +msgid "SADT Arrow" +msgstr "" + +#: objects/SADT/box.c:137 objects/flowchart/box.c:150 +#: objects/flowchart/diamond.c:148 objects/flowchart/ellipse.c:147 +#: objects/flowchart/parallelogram.c:152 +msgid "Text padding" +msgstr "ڿ ä" + +#: objects/SADT/box.c:144 +msgid "Activity/Data identifier" +msgstr "" + +#: objects/SADT/box.c:145 +msgid "The identifier which appears in the lower right corner of the Box" +msgstr "" + +#: objects/SADT/box.c:446 objects/Istar/other.c:484 +#: objects/Jackson/domain.c:540 objects/KAOS/goal.c:588 +#: objects/KAOS/other.c:536 objects/standard/line.c:233 +msgid "Delete connection point" +msgstr "" + +#: objects/SADT/box.c:451 +msgid "SADT box" +msgstr "" + +#: objects/SADT/sadt.c:41 +msgid "SADT diagram objects" +msgstr "" + +#: objects/UML/actor.c:359 objects/UML/actor.c:361 sheets/UML.sheet.in.h:2 +msgid "Actor" +msgstr "" + +#: objects/UML/association.c:1139 objects/UML/class_dialog.c:2107 +msgid "Direction:" +msgstr "" + +#: objects/UML/association.c:1156 +msgid "From A to B" +msgstr "A B" + +#: objects/UML/association.c:1163 +msgid "From B to A" +msgstr "B A" + +#: objects/UML/association.c:1187 objects/UML/association.c:1195 +msgid "Side A" +msgstr "A " + +#: objects/UML/association.c:1189 objects/UML/association.c:1197 +msgid "Side B" +msgstr "B " + +#: objects/UML/association.c:1203 +msgid "Role:" +msgstr ":" + +#: objects/UML/association.c:1215 +msgid "Multiplicity:" +msgstr "ߺ" + +#. Show arrow: +#: objects/UML/association.c:1226 +msgid "Show arrow" +msgstr "ȭǥ " + +#. Aggregate +#: objects/UML/association.c:1232 +msgid "Aggregate" +msgstr "" + +#. Composition +#: objects/UML/association.c:1240 +msgid "Composition" +msgstr "ռ" + +#: objects/UML/class.c:110 objects/UML/class.c:247 objects/UML/class.c:1163 +#: sheets/UML.sheet.in.h:6 +msgid "Class" +msgstr "Ŭ" + +#: objects/UML/class.c:114 objects/UML/classicon.c:135 +#: objects/UML/component.c:122 objects/UML/large_package.c:126 +#: objects/UML/object.c:147 objects/UML/small_package.c:122 +msgid "Stereotype" +msgstr "" + +#: objects/UML/class.c:116 objects/UML/class.c:153 +#: objects/UML/class_dialog.c:382 sheets/SDL.sheet.in.h:3 +#, fuzzy +msgid "Comment" +msgstr "߰" + +#: objects/UML/class.c:118 objects/UML/class.c:147 +#: objects/UML/class_dialog.c:296 objects/UML/class_dialog.c:367 +#: objects/UML/class_dialog.c:1933 +msgid "Abstract" +msgstr "" + +#: objects/UML/class.c:120 +#, fuzzy +msgid "Template" +msgstr "ø" + +#: objects/UML/class.c:123 objects/UML/class_dialog.c:305 +msgid "Suppress Attributes" +msgstr "Ӽ Ⱥ" + +#: objects/UML/class.c:125 +#, fuzzy +msgid "Suppress Operations" +msgstr "۵ Ⱥ" + +#: objects/UML/class.c:127 +#, fuzzy +msgid "Visible Attributes" +msgstr "Ӽ" + +#: objects/UML/class.c:129 +#, fuzzy +msgid "Visible Operations" +msgstr "۵" + +#: objects/UML/class.c:131 +#, fuzzy +msgid "Visible Comments" +msgstr "۵" + +#: objects/UML/class.c:133 objects/UML/class_dialog.c:321 +#, fuzzy +msgid "Wrap Operations" +msgstr "۵" + +#: objects/UML/class.c:135 +msgid "Wrap after char" +msgstr "" + +#: objects/UML/class.c:145 objects/UML/class_dialog.c:362 +msgid "Polymorphic" +msgstr "" + +#: objects/UML/class.c:149 +#, fuzzy +msgid "Classname" +msgstr "Ŭ̸:" + +#: objects/UML/class.c:151 +#, fuzzy +msgid "Abstract Classname" +msgstr "߻" + +#: objects/UML/class.c:157 objects/UML/class.c:159 objects/UML/class.c:161 +#: objects/UML/class.c:163 objects/UML/class.c:165 objects/UML/class.c:167 +msgid " " +msgstr "" + +#: objects/UML/class.c:172 objects/UML/object.c:151 +msgid "Attributes" +msgstr "Ӽ" + +#: objects/UML/class.c:174 +msgid "Operations" +msgstr "۵" + +#: objects/UML/class.c:241 +#, fuzzy +msgid "Show Comments" +msgstr "߰" + +#. Class page: +#: objects/UML/class_dialog.c:265 +#, fuzzy +msgid "_Class" +msgstr "Ŭ" + +#: objects/UML/class_dialog.c:273 +msgid "Class name:" +msgstr "Ŭ̸:" + +#: objects/UML/class_dialog.c:281 objects/UML/class_dialog.c:1856 +#: objects/UML/dependency.c:135 objects/UML/generalization.c:131 +#: objects/UML/realizes.c:132 +msgid "Stereotype:" +msgstr "" + +#: objects/UML/class_dialog.c:288 objects/UML/class_dialog.c:933 +#: objects/UML/class_dialog.c:1974 objects/UML/class_dialog.c:2096 +#, fuzzy +msgid "Comment:" +msgstr "߰" + +#: objects/UML/class_dialog.c:302 +msgid "Attributes visible" +msgstr "Ӽ " + +#: objects/UML/class_dialog.c:311 +msgid "Operations visible" +msgstr "۵ " + +#: objects/UML/class_dialog.c:314 +msgid "Suppress operations" +msgstr "۵ Ⱥ" + +#: objects/UML/class_dialog.c:328 +msgid "Wrap after this length: " +msgstr "" + +#: objects/UML/class_dialog.c:335 +#, fuzzy +msgid "Comments visible" +msgstr "۵ " + +#. head line +#: objects/UML/class_dialog.c:348 +msgid "Kind" +msgstr "" + +#: objects/UML/class_dialog.c:353 +#, fuzzy +msgid "Size" +msgstr "B " + +#: objects/UML/class_dialog.c:372 +#, fuzzy +msgid "Class Name" +msgstr "Ŭ̸:" + +#: objects/UML/class_dialog.c:377 +#, fuzzy +msgid "Abstract Class" +msgstr "߻" + +#. should probably be refactored too. +#: objects/UML/class_dialog.c:394 +#, fuzzy +msgid "Text Color" +msgstr " " + +#: objects/UML/class_dialog.c:402 +#, fuzzy +msgid "Foreground Color" +msgstr " :" + +#: objects/UML/class_dialog.c:410 +#, fuzzy +msgid "Background Color" +msgstr " :" + +#. Attributes page: +#: objects/UML/class_dialog.c:833 +#, fuzzy +msgid "_Attributes" +msgstr "Ӽ" + +#: objects/UML/class_dialog.c:861 objects/UML/class_dialog.c:1792 +#: objects/UML/class_dialog.c:2020 objects/UML/class_dialog.c:2540 +#, fuzzy +msgid "_New" +msgstr "/(_V)" + +#: objects/UML/class_dialog.c:867 objects/UML/class_dialog.c:1798 +#: objects/UML/class_dialog.c:2027 objects/UML/class_dialog.c:2546 +msgid "_Delete" +msgstr "" + +#: objects/UML/class_dialog.c:873 objects/UML/class_dialog.c:1804 +#: objects/UML/class_dialog.c:2034 objects/UML/class_dialog.c:2552 +msgid "Move up" +msgstr " ̵" + +#: objects/UML/class_dialog.c:879 objects/UML/class_dialog.c:1810 +#: objects/UML/class_dialog.c:2041 objects/UML/class_dialog.c:2558 +msgid "Move down" +msgstr "Ʒ ̵" + +#: objects/UML/class_dialog.c:890 +msgid "Attribute data" +msgstr "Ӽ " + +#: objects/UML/class_dialog.c:922 +msgid "Value:" +msgstr ":" + +#: objects/UML/class_dialog.c:945 objects/UML/class_dialog.c:1869 +msgid "Visibility:" +msgstr "ǥ:" + +#: objects/UML/class_dialog.c:954 objects/UML/class_dialog.c:1878 +msgid "Public" +msgstr "" + +#: objects/UML/class_dialog.c:962 objects/UML/class_dialog.c:1886 +msgid "Private" +msgstr "" + +#: objects/UML/class_dialog.c:970 objects/UML/class_dialog.c:1894 +msgid "Protected" +msgstr "" + +#: objects/UML/class_dialog.c:978 objects/UML/class_dialog.c:1902 +msgid "Implementation" +msgstr "" + +#: objects/UML/class_dialog.c:999 objects/UML/class_dialog.c:1918 +msgid "Class scope" +msgstr "" + +#. Operations page: +#: objects/UML/class_dialog.c:1764 +#, fuzzy +msgid "_Operations" +msgstr "۵" + +#: objects/UML/class_dialog.c:1822 +msgid "Operation data" +msgstr "۵ " + +#: objects/UML/class_dialog.c:1924 +msgid "Inheritance type:" +msgstr "" + +#: objects/UML/class_dialog.c:1941 +msgid "Polymorphic (virtual)" +msgstr "" + +#: objects/UML/class_dialog.c:1949 +msgid "Leaf (final)" +msgstr "" + +#: objects/UML/class_dialog.c:1967 +msgid "Query" +msgstr "" + +#: objects/UML/class_dialog.c:1991 +msgid "Parameters:" +msgstr ":" + +#: objects/UML/class_dialog.c:2053 +msgid "Parameter data" +msgstr " " + +#: objects/UML/class_dialog.c:2085 +msgid "Def. value:" +msgstr "⺻ :" + +#: objects/UML/class_dialog.c:2116 +#, fuzzy +msgid "Undefined" +msgstr "" + +#: objects/UML/class_dialog.c:2124 +msgid "In" +msgstr "" + +#: objects/UML/class_dialog.c:2133 +msgid "Out" +msgstr "" + +#: objects/UML/class_dialog.c:2142 +msgid "In & Out" +msgstr "" + +#. Templates page: +#: objects/UML/class_dialog.c:2506 +#, fuzzy +msgid "_Templates" +msgstr "ø" + +#: objects/UML/class_dialog.c:2512 sheets/UML.sheet.in.h:29 +msgid "Template class" +msgstr "ø Ŭ" + +#: objects/UML/class_dialog.c:2569 +msgid "Formal parameter data" +msgstr "" + +#: objects/UML/classicon.c:125 +msgid "Boundary" +msgstr "" + +#: objects/UML/classicon.c:137 +#, fuzzy +msgid "Is object" +msgstr "ü Ȱ" + +#: objects/UML/component_feature.c:144 sheets/UML.sheet.in.h:14 +msgid "Facet" +msgstr "" + +#: objects/UML/component_feature.c:145 sheets/UML.sheet.in.h:26 +msgid "Receptacle" +msgstr "" + +#: objects/UML/component_feature.c:146 sheets/UML.sheet.in.h:13 +msgid "Event Source" +msgstr "" + +#: objects/UML/component_feature.c:147 sheets/UML.sheet.in.h:12 +msgid "Event Sink" +msgstr "" + +#: objects/UML/constraint.c:125 +msgid "Constraint:" +msgstr "" + +#: objects/UML/dependency.c:137 +msgid "Show arrow:" +msgstr "" + +#: objects/UML/implements.c:128 +msgid "Interface:" +msgstr "" + +#: objects/UML/lifeline.c:144 +msgid "Draw focus of control:" +msgstr "" + +#: objects/UML/lifeline.c:146 +msgid "Draw destruction mark:" +msgstr "" + +#: objects/UML/lifeline.c:417 +#, fuzzy +msgid "Add connection points" +msgstr ":" + +#: objects/UML/lifeline.c:418 +#, fuzzy +msgid "Remove connection points" +msgstr " (_C)" + +#: objects/UML/lifeline.c:422 +msgid "UML Lifeline" +msgstr "" + +#: objects/UML/message.c:135 +msgid "Call" +msgstr "ȣ" + +#: objects/UML/message.c:136 +msgid "Create" +msgstr "" + +#: objects/UML/message.c:137 +msgid "Destroy" +msgstr "Ҹ" + +#: objects/UML/message.c:138 +msgid "Simple" +msgstr "" + +#: objects/UML/message.c:139 +msgid "Return" +msgstr "" + +#: objects/UML/message.c:140 +msgid "Send" +msgstr "" + +#: objects/UML/message.c:141 +msgid "Recursive" +msgstr "" + +#: objects/UML/message.c:150 objects/Jackson/phenomenon.c:142 +msgid "Message:" +msgstr "" + +#: objects/UML/message.c:152 +msgid "Message type:" +msgstr "" + +#: objects/UML/object.c:149 +msgid "Explicit state" +msgstr "" + +#: objects/UML/object.c:154 +msgid "Active object" +msgstr "ü Ȱ" + +#: objects/UML/object.c:156 +msgid "Show attributes" +msgstr "" + +#: objects/UML/object.c:158 +#, fuzzy +msgid "Multiple instance" +msgstr "ߺ" + +#. Would like to create a state_term instead, but making the connections +#. * is a pain +#: objects/UML/state.c:410 +msgid "" +"This diagram uses the State object for initial/final states.\n" +"That option will go away in future versions.\n" +"Please use the Initial/Final State object instead\n" +msgstr "" + +#: objects/UML/state_term.c:118 +msgid "Is final" +msgstr "" + +#: objects/UML/uml.c:64 +msgid "Unified Modelling Language diagram objects" +msgstr "" + +#: objects/UML/usecase.c:130 +msgid "Text outside" +msgstr "" + +#: objects/UML/usecase.c:132 +msgid "Collaboration" +msgstr "" + +#: objects/bondgraph/bondgraph.c:39 +#, fuzzy +msgid "Bond graph objects" +msgstr "ǥ ü" + +#: objects/chronogram/chronogram.c:40 +msgid "Chronogram diagram objects" +msgstr "" + +#: objects/chronogram/chronoline.c:148 +msgid "Data" +msgstr "" + +#: objects/chronogram/chronoline.c:150 +#, fuzzy +msgid "Data name" +msgstr "Ŭ̸:" + +#: objects/chronogram/chronoline.c:152 +msgid "Events" +msgstr "" + +#: objects/chronogram/chronoline.c:155 +msgid "Event specification" +msgstr "" + +#: objects/chronogram/chronoline.c:156 +msgid "" +"@ time set the pointer at an absolute time.\n" +"( duration sets the signal up, then wait 'duration'.\n" +") duration sets the signal down, then wait 'duration'.\n" +"u duration sets the signal to \"unknown\" state, then wait 'duration'.\n" +"example : @ 1.0 (2.0)1.0(2.0)\n" +msgstr "" + +#: objects/chronogram/chronoline.c:162 +msgid "Parameters" +msgstr "" + +#: objects/chronogram/chronoline.c:164 objects/chronogram/chronoref.c:146 +#, fuzzy +msgid "Start time" +msgstr "ȭǥ " + +#: objects/chronogram/chronoline.c:166 objects/chronogram/chronoref.c:148 +msgid "End time" +msgstr "" + +#: objects/chronogram/chronoline.c:168 +#, fuzzy +msgid "Rise time" +msgstr " ø" + +#: objects/chronogram/chronoline.c:170 +msgid "Fall time" +msgstr "" + +#: objects/chronogram/chronoline.c:171 +msgid "Multi-bit data" +msgstr "" + +#: objects/chronogram/chronoline.c:173 objects/chronogram/chronoref.c:156 +msgid "Aspect" +msgstr "" + +#: objects/chronogram/chronoline.c:175 +#, fuzzy +msgid "Data color" +msgstr " " + +#: objects/chronogram/chronoline.c:177 +#, fuzzy +msgid "Data line width" +msgstr " " + +#: objects/chronogram/chronoref.c:144 +#, fuzzy +msgid "Time data" +msgstr "Ӽ " + +#: objects/chronogram/chronoref.c:150 +msgid "Major time step" +msgstr "" + +#: objects/chronogram/chronoref.c:152 +msgid "Minor time step" +msgstr "" + +#: objects/chronogram/chronoref.c:162 +msgid "Minor step line width" +msgstr "" + +#: objects/custom/custom.c:117 +msgid "Custom" +msgstr "" + +#: objects/custom/custom.c:117 +msgid "Custom XML shapes loader" +msgstr "" + +#: objects/custom/custom_object.c:178 objects/custom/custom_object.c:197 +msgid "Flip horizontal" +msgstr "Ʒ ٲ" + +#: objects/custom/custom_object.c:180 objects/custom/custom_object.c:199 +msgid "Flip vertical" +msgstr "¿ ٲ" + +#: objects/custom/custom_object.c:1431 +msgid "Flip Horizontal" +msgstr "Ʒ ٲ" + +#: objects/custom/custom_object.c:1432 +msgid "Flip Vertical" +msgstr "¿ ٲ" + +#: objects/custom/custom_object.c:1469 +#, c-format +msgid "Cannot open icon file %s for object type '%s'." +msgstr "" + +#: objects/flowchart/box.c:148 objects/standard/box.c:148 +#: objects/standard/polyline.c:130 objects/standard/zigzagline.c:129 +msgid "Corner radius" +msgstr "ڸ " + +#: objects/flowchart/flowchart.c:37 +msgid "Flowchart objects" +msgstr "帧 ü" + +#: objects/flowchart/parallelogram.c:150 +#, fuzzy +msgid "Shear angle" +msgstr " :" + +#: objects/Istar/actor.c:66 objects/Istar/link.c:155 +#, fuzzy +msgid "Unspecified" +msgstr "" + +#: objects/Istar/actor.c:67 objects/KAOS/other.c:75 +#, fuzzy +msgid "Agent" +msgstr ":" + +#: objects/Istar/actor.c:69 +#, fuzzy +msgid "Role" +msgstr ":" + +#: objects/Istar/goal.c:74 objects/KAOS/goal.c:79 +msgid "Softgoal" +msgstr "" + +#: objects/Istar/goal.c:75 objects/KAOS/goal.c:80 +msgid "Goal" +msgstr "" + +#: objects/Istar/goal.c:155 objects/Istar/goal.c:156 objects/KAOS/goal.c:159 +#: objects/KAOS/goal.c:160 +#, fuzzy +msgid "Goal Type" +msgstr "// " + +#: objects/Istar/istar.c:46 +#, fuzzy +msgid "Istar diagram" +msgstr "ǥ μ" + +#: objects/Istar/link.c:156 +#, fuzzy +msgid "Positive Contrib" +msgstr "ռ" + +#: objects/Istar/link.c:157 +#, fuzzy +msgid "Negative contrib" +msgstr " " + +#: objects/Istar/link.c:158 sheets/UML.sheet.in.h:10 +#, fuzzy +msgid "Dependency" +msgstr " " + +#: objects/Istar/link.c:159 +#, fuzzy +msgid "Decomposition" +msgstr "ռ" + +#: objects/Istar/link.c:160 +msgid "Means-Ends" +msgstr "" + +#: objects/Istar/other.c:73 +#, fuzzy +msgid "Resource" +msgstr "" + +#: objects/Istar/other.c:74 +msgid "Task" +msgstr "" + +#: objects/Istar/other.c:489 +msgid "i* other" +msgstr "" + +#: objects/Jackson/domain.c:79 +msgid "Given Domain" +msgstr "" + +#: objects/Jackson/domain.c:80 +msgid "Designed Domain" +msgstr "" + +#: objects/Jackson/domain.c:81 +msgid "Machine Domain" +msgstr "" + +#: objects/Jackson/domain.c:97 +#, fuzzy +msgid "Causal" +msgstr "ȣ" + +#: objects/Jackson/domain.c:98 +#, fuzzy +msgid "Biddable" +msgstr "" + +#: objects/Jackson/domain.c:99 +msgid "Lexical" +msgstr "" + +#: objects/Jackson/domain.c:178 objects/Jackson/domain.c:179 +msgid "Domain Type" +msgstr "" + +#: objects/Jackson/domain.c:183 +msgid "Domain Kind" +msgstr "" + +#: objects/Jackson/domain.c:184 +msgid "Optional kind which appears in the lower right corner of the Domain" +msgstr "" + +#: objects/Jackson/domain.c:545 +msgid "Jackson domain" +msgstr "" + +#: objects/Jackson/jackson.c:47 +#, fuzzy +msgid "Jackson diagram" +msgstr " ǥ ϴ" + +#: objects/Jackson/phenomenon.c:134 +#, fuzzy +msgid "Shared" +msgstr "" + +#: objects/Jackson/phenomenon.c:135 objects/KAOS/goal.c:81 +msgid "Requirement" +msgstr "" + +#: objects/KAOS/goal.c:82 +msgid "Assumption" +msgstr "" + +#: objects/KAOS/goal.c:83 +msgid "Obstacle" +msgstr "" + +#: objects/KAOS/goal.c:593 +msgid "KAOS goal" +msgstr "" + +#: objects/KAOS/kaos.c:49 +#, fuzzy +msgid "KAOS diagram" +msgstr " ǥ(_N)" + +#: objects/KAOS/metaandorrel.c:151 +#, fuzzy +msgid "AND Refinement" +msgstr "" + +#: objects/KAOS/metaandorrel.c:152 +msgid "Complete AND Refinement" +msgstr "" + +#: objects/KAOS/metaandorrel.c:153 +#, fuzzy +msgid "OR Refinement" +msgstr "" + +#: objects/KAOS/metaandorrel.c:154 +#, fuzzy +msgid "Operationalization" +msgstr "۵ " + +#: objects/KAOS/metaandorrel.c:161 +#, fuzzy +msgid "Text:" +msgstr "ڿ" + +#: objects/KAOS/metabinrel.c:165 +#, fuzzy +msgid "Contributes" +msgstr "ֽźе:" + +#: objects/KAOS/metabinrel.c:166 +#, fuzzy +msgid "Obstructs" +msgstr "߻" + +#: objects/KAOS/metabinrel.c:167 +msgid "Conflicts" +msgstr "" + +#: objects/KAOS/metabinrel.c:168 +#, fuzzy +msgid "Responsibility" +msgstr "ǥ:" + +#: objects/KAOS/metabinrel.c:169 +#, fuzzy +msgid "Monitors" +msgstr "ռ" + +#: objects/KAOS/metabinrel.c:170 +#, fuzzy +msgid "Controls" +msgstr " " + +#: objects/KAOS/metabinrel.c:171 +msgid "CapableOf" +msgstr "" + +#: objects/KAOS/metabinrel.c:172 +msgid "Performs" +msgstr "" + +#: objects/KAOS/metabinrel.c:174 +msgid "Output" +msgstr "" + +#: objects/KAOS/other.c:541 +#, fuzzy +msgid "KAOS other" +msgstr "ڸ ϱ" + +#: objects/network/basestation.c:133 +#, fuzzy +msgid "Sectors" +msgstr " " + +#: objects/network/basestation.c:388 objects/network/basestation.c:390 +#, fuzzy +msgid "Base Station" +msgstr "ȭǥ " + +#: objects/network/bus.c:599 +msgid "Add Handle" +msgstr "ڵ ߰" + +#: objects/network/bus.c:600 +msgid "Delete Handle" +msgstr "ڵ " + +#: objects/network/network.c:43 +msgid "Network diagram objects" +msgstr "Ʈ ǥ ü" + +#: objects/network/radiocell.c:130 +msgid "Macro Cell" +msgstr "" + +#: objects/network/radiocell.c:131 +msgid "Micro Cell" +msgstr "" + +#: objects/network/radiocell.c:132 +msgid "Pico Cell" +msgstr "" + +#: objects/network/radiocell.c:138 +msgid "Radius" +msgstr "" + +#: objects/network/radiocell.c:140 +#, fuzzy +msgid "Cell Type:" +msgstr ":" + +#: objects/network/radiocell.c:152 +msgid "Subscribers" +msgstr "" + +#: objects/standard/arc.c:131 +msgid "Curve distance" +msgstr "" + +#: objects/standard/bezier.c:545 objects/standard/beziergon.c:499 +msgid "Add Segment" +msgstr "κ ߰" + +#: objects/standard/bezier.c:546 objects/standard/beziergon.c:500 +msgid "Delete Segment" +msgstr "κ " + +#: objects/standard/bezier.c:548 objects/standard/beziergon.c:502 +msgid "Symmetric control" +msgstr "Ī " + +#: objects/standard/bezier.c:550 objects/standard/beziergon.c:504 +msgid "Smooth control" +msgstr "ε巯 " + +#: objects/standard/bezier.c:552 objects/standard/beziergon.c:506 +msgid "Cusp control" +msgstr " " + +#: objects/standard/box.c:135 objects/standard/ellipse.c:131 +#, fuzzy +msgid "Free" +msgstr "" + +#: objects/standard/box.c:136 objects/standard/ellipse.c:132 +#, fuzzy +msgid "Fixed" +msgstr "" + +#: objects/standard/box.c:137 objects/standard/box.c:687 +msgid "Square" +msgstr "" + +#: objects/standard/box.c:150 objects/standard/ellipse.c:144 +#, fuzzy +msgid "Aspect ratio" +msgstr " " + +#: objects/standard/box.c:683 objects/standard/ellipse.c:664 +#, fuzzy +msgid "Free aspect" +msgstr " " + +#: objects/standard/box.c:685 objects/standard/ellipse.c:666 +msgid "Fixed aspect" +msgstr "" + +#: objects/standard/ellipse.c:133 objects/standard/ellipse.c:668 +#, fuzzy +msgid "Circle" +msgstr "" + +#: objects/standard/image.c:130 +msgid "Image file" +msgstr "׸ " + +#: objects/standard/image.c:132 +msgid "Draw border" +msgstr "ڸ ׸" + +#: objects/standard/image.c:134 +msgid "Keep aspect ratio" +msgstr " " + +#. Found file in same dir as diagram. +#. Found file in current dir. +#: objects/standard/image.c:669 objects/standard/image.c:680 +#, c-format +msgid "" +"The image file '%s' was not found in that directory.\n" +"Using the file '%s' instead\n" +msgstr "" +" ڷ濡 ׸ '%s' ã ϴ.\n" +"ſ '%s' ̿մϴ\n" + +#. Didn't find file in current dir. +#: objects/standard/image.c:686 objects/standard/image.c:714 +#, c-format +msgid "The image file '%s' was not found.\n" +msgstr "׸ '%s' ã ϴ.\n" + +#: objects/standard/line.c:135 +#, fuzzy +msgid "Arrows" +msgstr "ȭǥ " + +#: objects/standard/line.c:140 +msgid "Start point" +msgstr "" + +#: objects/standard/line.c:142 +msgid "End point" +msgstr "" + +#: objects/standard/polygon.c:460 objects/standard/polyline.c:515 +msgid "Add Corner" +msgstr "ڸ ϱ" + +#: objects/standard/polygon.c:461 objects/standard/polyline.c:516 +msgid "Delete Corner" +msgstr "ڸ " + +#: objects/standard/standard.c:45 +msgid "Standard objects" +msgstr "ǥ ü" + +#: plug-ins/cairo/diacairo.c:967 +msgid "Cairo PostScript" +msgstr "" + +#: plug-ins/cairo/diacairo.c:976 +msgid "Cairo PNG" +msgstr "" + +#: plug-ins/cairo/diacairo.c:1004 +msgid "Cairo based Rendering" +msgstr "" + +#: plug-ins/cgm/cgm.c:1109 +msgid "" +"Image row length larger than maximum cell array.\n" +"Image not exported to CGM." +msgstr "" +"׸ ̰ ִ Ѿϴ.\n" +"׸ CGM Ҽ ϴ." + +#: plug-ins/cgm/cgm.c:1365 +msgid "Computer Graphics Metafile" +msgstr "ǻ ׷ Ÿ" + +#: plug-ins/cgm/cgm.c:1379 +msgid "Computer Graphics Metafile export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/dxf/dxf-export.c:547 plug-ins/dxf/dxf-import.c:1379 +msgid "Drawing Interchange File" +msgstr "" + +#: plug-ins/dxf/dxf-import.c:481 plug-ins/dxf/dxf-import.c:498 +msgid "Bad vertex bulge\n" +msgstr "" + +#: plug-ins/dxf/dxf-import.c:1051 +#, fuzzy, c-format +msgid "Scale: %f\n" +msgstr ":" + +#: plug-ins/dxf/dxf-import.c:1313 +msgid "read_dxf_codes failed\n" +msgstr "" + +#: plug-ins/dxf/dxf-import.c:1345 +#, c-format +msgid "Unknown dxf code %d\n" +msgstr "" + +#: plug-ins/dxf/dxf.c:37 +msgid "Drawing Interchange File import and export filters" +msgstr "" + +#: plug-ins/hpgl/hpgl.c:772 +#, fuzzy +msgid "HP Graphics Language" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/hpgl/hpgl.c:786 +#, fuzzy +msgid "HP Graphics Language export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/metapost/metapost.c:34 +#, fuzzy +msgid "TeX Metapost export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/metapost/render_metapost.c:1033 +msgid "TeX Metapost macros" +msgstr "" + +#: plug-ins/pixbuf/pixbuf.c:95 +#, fuzzy, c-format +msgid "" +"Could not save file:\n" +"%s\n" +"%s" +msgstr "`%s' ϴ" + +#: plug-ins/pixbuf/pixbuf.c:159 +msgid "GdkPixbuf - not antialiased" +msgstr "" + +#: plug-ins/pixbuf/pixbuf.c:169 +msgid "GdkPixbuf bitmap" +msgstr "" + +#: plug-ins/pixbuf/pixbuf.c:189 +msgid "gdk-pixbuf based bitmap export/import" +msgstr "" + +#: plug-ins/pstricks/pstricks.c:14 +#, fuzzy +msgid "TeX Pstricks export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/pstricks/render_pstricks.c:653 +msgid "Not valid UTF8" +msgstr "" + +#: plug-ins/pstricks/render_pstricks.c:871 +msgid "TeX PSTricks macros" +msgstr "" + +#: plug-ins/python/pydia-render.c:711 plug-ins/wmf/wmf.cpp:1137 +#, c-format +msgid "Couldn't open: '%s' for writing.\n" +msgstr " ϴ: '%s' .\n" + +#: plug-ins/python/python.c:92 +msgid "Python scripting support" +msgstr "" + +#: plug-ins/shape/shape-export.c:470 +msgid "Can't export png without libart!" +msgstr "" + +#: plug-ins/shape/shape-export.c:497 +msgid "Dia Shape File" +msgstr "" + +#: plug-ins/shape/shape.c:36 +#, fuzzy +msgid "dia shape export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/svg/render_svg.c:301 plug-ins/svg/svg-import.c:641 +msgid "Scalable Vector Graphics" +msgstr "" + +#: plug-ins/svg/svg-import.c:203 plug-ins/svg/svg-import.c:217 +#: plug-ins/xfig/xfig-import.c:108 plug-ins/xfig/xfig-import.c:159 +#: plug-ins/xfig/xfig-import.c:188 plug-ins/xfig/xfig-import.c:224 +#: plug-ins/xfig/xfig-import.c:261 plug-ins/xfig/xfig-import.c:289 +#: plug-ins/xfig/xfig-import.c:326 plug-ins/xfig/xfig-import.c:362 +#: plug-ins/xfig/xfig-import.c:403 +#, fuzzy +msgid "Can't find standard object" +msgstr "ǥ ü" + +#: plug-ins/svg/svg-import.c:229 +msgid "Unexpected SVG path element" +msgstr "" + +#: plug-ins/svg/svg-import.c:287 +msgid "Courier" +msgstr "" + +#: plug-ins/svg/svg-import.c:590 +msgid "Could not find SVG namespace." +msgstr "" + +#: plug-ins/svg/svg-import.c:596 +#, c-format +msgid "root element was '%s' -- expecting 'svg'." +msgstr "" + +#: plug-ins/svg/svg.c:37 +#, fuzzy +msgid "Scalable Vector Graphics import and export filters" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/wmf/wmf.cpp:1204 +msgid "Windows Meta File" +msgstr "" + +#: plug-ins/wmf/wmf.cpp:1220 +#, fuzzy +msgid "WMF export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/wpg/wpg.c:1182 +#, c-format +msgid "File: %s type/version unsupported.\n" +msgstr "" + +#: plug-ins/wpg/wpg.c:1312 plug-ins/wpg/wpg.c:1319 +msgid "WPG" +msgstr "" + +#: plug-ins/wpg/wpg.c:1333 +#, fuzzy +msgid "WordPerfect Graphics export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/xfig/xfig-export.c:454 +#, c-format +msgid "FIG format has no equivalent of arrow style %s, using simple arrow.\n" +msgstr "" + +#: plug-ins/xfig/xfig-export.c:475 +msgid "No more user-definable colors - using black" +msgstr "" + +#: plug-ins/xfig/xfig-export.c:1149 +msgid "XFig format" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:472 +msgid "Patterns are not supported by Dia" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:500 +msgid "Triple-dotted lines are not supported by Dia, using double-dotted" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:504 +#, fuzzy, c-format +msgid "Line style %d should not appear\n" +msgstr " Ӽ" + +#: plug-ins/xfig/xfig-import.c:573 +#, c-format +msgid "Error while reading %dth of %d points: %s\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:595 +#, fuzzy +msgid "Error while reading arrowhead\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:615 +#, c-format +msgid "Unknown arrow type %d\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:731 +#, fuzzy, c-format +msgid "Couldn't read ellipse info: %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#: plug-ins/xfig/xfig-import.c:799 +#, fuzzy, c-format +msgid "Couldn't read polyline info: %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#: plug-ins/xfig/xfig-import.c:814 +#, c-format +msgid "Couldn't read flipped bit: %s\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:832 +msgid "Negative corner radius, negating" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:876 plug-ins/xfig/xfig-import.c:1192 +#, c-format +msgid "Unknown polyline subtype: %d\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1026 plug-ins/xfig/xfig-import.c:1058 +#, fuzzy, c-format +msgid "Couldn't read spline info: %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#. Open approximated spline +#. Closed approximated spline +#: plug-ins/xfig/xfig-import.c:1045 plug-ins/xfig/xfig-import.c:1063 +msgid "Cannot convert approximated spline yet." +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1103 +#, c-format +msgid "Unknown spline subtype: %d\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1166 +#, fuzzy, c-format +msgid "Couldn't read arc info: %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#: plug-ins/xfig/xfig-import.c:1256 +#, c-format +msgid "Couldn't read text info: %s\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1319 +#, c-format +msgid "Couldn't identify FIG object: %s\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1327 +msgid "Compound end outside compound\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1346 +#, fuzzy, c-format +msgid "Couldn't read color: %s\n" +msgstr "" +"÷ `%s' ϴ\n" +"%s" + +#: plug-ins/xfig/xfig-import.c:1391 +#, fuzzy, c-format +msgid "Couldn't read group extend: %s\n" +msgstr "`%s' ϴ" + +#: plug-ins/xfig/xfig-import.c:1402 +#, c-format +msgid "Unknown object type %d\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1426 +#, c-format +msgid "`%s' is not one of `%s' or `%s'\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1436 +#, fuzzy, c-format +msgid "Error reading paper size: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1447 +#, c-format +msgid "Unknown paper size `%s', using default\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1459 +#, fuzzy, c-format +msgid "Error reading paper orientation: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1469 +#, fuzzy, c-format +msgid "Error reading justification: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1479 +#, fuzzy, c-format +msgid "Error reading units: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1493 +#, fuzzy, c-format +msgid "Error reading magnification: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1504 +#, fuzzy, c-format +msgid "Error reading multipage indicator: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1515 +#, fuzzy, c-format +msgid "Error reading transparent color: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1524 plug-ins/xfig/xfig-import.c:1579 +#: plug-ins/xfig/xfig-import.c:1597 +#, fuzzy, c-format +msgid "Error reading FIG file: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1526 plug-ins/xfig/xfig-import.c:1581 +msgid "Premature end of FIG file\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1535 +#, fuzzy, c-format +msgid "Error reading resolution: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xfig/xfig-import.c:1566 +#, c-format +msgid "Doesn't look like a Fig file: %s\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1572 +#, c-format +msgid "This is a FIG version %d.%d file, I may not understand it\n" +msgstr "" + +#: plug-ins/xfig/xfig-import.c:1620 +msgid "XFig File Format" +msgstr "" + +#: plug-ins/xfig/xfig.c:39 +#, fuzzy +msgid "Fig Format import and export filter" +msgstr "ǻ ׷ Ÿ " + +#: plug-ins/xslt/xslt.c:102 +#, fuzzy, c-format +msgid "Error while parsing %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xslt/xslt.c:110 +#, fuzzy, c-format +msgid "Error while parsing stylesheet %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xslt/xslt.c:116 +#, fuzzy, c-format +msgid "Error while applying stylesheet %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xslt/xslt.c:124 +#, fuzzy, c-format +msgid "Error while parsing stylesheet: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xslt/xslt.c:132 +#, fuzzy, c-format +msgid "Error while applying stylesheet: %s\n" +msgstr "ǥ д \n" + +#: plug-ins/xslt/xslt.c:313 +#, fuzzy +msgid "XSL Transformation filter" +msgstr "" + +#: plug-ins/xslt/xslt.c:337 +msgid "No valid configuration files found for the XSLT plugin, not loading." +msgstr "" + +#: plug-ins/xslt/xsltdialog.c:99 +msgid "Export through XSLT" +msgstr "" + +#: plug-ins/xslt/xsltdialog.c:114 +msgid "From:" +msgstr "" + +#: plug-ins/xslt/xsltdialog.c:149 +#, fuzzy +msgid "To:" +msgstr ":" + +#: sheets/Assorted.sheet.in.h:1 +msgid "" +"An Assorted Collection of Polygons, Beziergons and other Miscellaneous " +"Geometric Shapes" +msgstr "" + +#: sheets/Assorted.sheet.in.h:2 +msgid "Assorted" +msgstr "" + +#: sheets/Assorted.sheet.in.h:3 +msgid "Chevron" +msgstr "" + +#: sheets/Assorted.sheet.in.h:4 +msgid "Curved eight point star" +msgstr "" + +#: sheets/Assorted.sheet.in.h:5 +msgid "Curved four point star" +msgstr "" + +#: sheets/Assorted.sheet.in.h:6 +#, fuzzy +msgid "Diamond" +msgstr "̸:" + +#: sheets/Assorted.sheet.in.h:7 +#, fuzzy +msgid "Down arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:8 +msgid "Eight point star" +msgstr "" + +#: sheets/Assorted.sheet.in.h:9 +#, fuzzy +msgid "Four point star" +msgstr "ε巯 " + +#: sheets/Assorted.sheet.in.h:10 +#, fuzzy +msgid "Heart" +msgstr "" + +#: sheets/Assorted.sheet.in.h:11 +msgid "Heptagon. Seven sided shape" +msgstr "" + +#: sheets/Assorted.sheet.in.h:12 +msgid "Hexagon. Six sided shape" +msgstr "" + +#: sheets/Assorted.sheet.in.h:13 +#, fuzzy +msgid "Horizontal parallelogram" +msgstr "Ʒ ٲ" + +#: sheets/Assorted.sheet.in.h:14 +msgid "Isoceles triangle" +msgstr "" + +#: sheets/Assorted.sheet.in.h:15 +#, fuzzy +msgid "Left arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:16 +#, fuzzy +msgid "Left-right arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:17 +#, fuzzy +msgid "Left-right-up arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:18 +msgid "Left-up arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:19 +msgid "Maltese cross" +msgstr "" + +#: sheets/Assorted.sheet.in.h:20 +msgid "Notched left arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:21 +#, fuzzy +msgid "Notched right arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:22 +msgid "Octogon. Eight sided shape" +msgstr "" + +#: sheets/Assorted.sheet.in.h:23 +msgid "Pentagon block arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:24 +msgid "Pentagon. Five sided shape" +msgstr "" + +#: sheets/Assorted.sheet.in.h:25 +msgid "Perfect circle" +msgstr "" + +#: sheets/Assorted.sheet.in.h:26 +msgid "Perfect square, height equals width" +msgstr "" + +#: sheets/Assorted.sheet.in.h:27 +#, fuzzy +msgid "Quad arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:28 +msgid "Quarter circle" +msgstr "" + +#: sheets/Assorted.sheet.in.h:29 +msgid "Quarter moon" +msgstr "" + +#: sheets/Assorted.sheet.in.h:30 +msgid "Right angle triangle" +msgstr "" + +#: sheets/Assorted.sheet.in.h:31 +#, fuzzy +msgid "Right arrow" +msgstr "" + +#: sheets/Assorted.sheet.in.h:32 +#, fuzzy +msgid "Seven point star" +msgstr " " + +#: sheets/Assorted.sheet.in.h:33 +msgid "Sharp eight point star" +msgstr "" + +#: sheets/Assorted.sheet.in.h:34 +#, fuzzy +msgid "Six point star" +msgstr " " + +#: sheets/Assorted.sheet.in.h:35 +msgid "Sun" +msgstr "" + +#: sheets/Assorted.sheet.in.h:36 +#, fuzzy +msgid "Swiss cross" +msgstr "Ŭ" + +#: sheets/Assorted.sheet.in.h:37 +msgid "Trapezoid" +msgstr "" + +#: sheets/Assorted.sheet.in.h:38 +#, fuzzy +msgid "Turn-up arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:39 +#, fuzzy +msgid "Up arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:40 +#, fuzzy +msgid "Up-down arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:41 +#, fuzzy +msgid "Up-down-left arrow" +msgstr "ȭǥ " + +#: sheets/Assorted.sheet.in.h:42 +#, fuzzy +msgid "Vertical parallelogram" +msgstr " (_H)" + +#: sheets/ChemEng.sheet.in.h:1 +#, fuzzy +msgid "AC Generator" +msgstr " ̽" + +#: sheets/ChemEng.sheet.in.h:2 +#, fuzzy +msgid "Air Cooler" +msgstr "ڸ ϱ" + +#: sheets/ChemEng.sheet.in.h:3 +msgid "Autoclave" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:4 +msgid "Axial Flow Fan" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:5 +#, fuzzy +msgid "Basic Filter" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:6 +msgid "Centrifugal Pump or Fan" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:7 +#, fuzzy +msgid "Centrifuge" +msgstr "߰" + +#: sheets/ChemEng.sheet.in.h:8 +#, fuzzy +msgid "ChemEng" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:9 +msgid "Clarifier or Settling Tank" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:10 +msgid "Collection for chemical engineering" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:11 +#, fuzzy +msgid "Compressor or Turbine" +msgstr " :" + +#: sheets/ChemEng.sheet.in.h:12 +msgid "Covered tank" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:13 +msgid "Cyclone and hydrocyclone" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:14 +msgid "Double-Pipe Exchanger" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:15 +msgid "Ejector or Injector" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:16 +msgid "Fan or Stirrer" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:17 +msgid "Fixed-Sheet Exchanger" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:18 +msgid "Floating-Head or U-Tube Exchanger" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:19 +msgid "Fluid Contacting Vessel, simple" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:20 +msgid "Forced-Flow Air Cooler" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:21 +msgid "Gas Holder, basic" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:22 +msgid "Heating/Cooling Coil" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:23 +msgid "Heating/Cooling Coil, vertical" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:24 +msgid "Induced-Flow Air Cooler" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:25 +msgid "Kettle Reboiler" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:26 +msgid "Knock-out Drum (with demister pad)" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:27 +msgid "Measurement" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:28 +#, fuzzy +msgid "Mixer" +msgstr "ߺ" + +#: sheets/ChemEng.sheet.in.h:29 +msgid "Open Tank" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:30 +msgid "Plate Exchanger" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:31 +#, fuzzy +msgid "Pneumatic Line" +msgstr " " + +#: sheets/ChemEng.sheet.in.h:32 +msgid "Pneumatic Line, vertical" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:33 +msgid "Positive Displacement Rotary Pump or Compressor" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:34 +msgid "Reactor or Absorption Vessel, simple" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:35 +msgid "Reciprocating Compressor or Pump" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:36 +#, fuzzy +msgid "Regulable Valve" +msgstr "ø" + +#: sheets/ChemEng.sheet.in.h:37 +msgid "Regulable Valve, vertical" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:38 +msgid "Sealed Tank" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:39 +#, fuzzy +msgid "Simple Furnace" +msgstr " " + +#: sheets/ChemEng.sheet.in.h:40 +msgid "Simple Heat Exchanger" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:41 +msgid "Simple Heat Exchanger, vertical" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:42 +msgid "Simple Vessel" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:43 +msgid "Spray Drier" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:44 +msgid "Spraying Device" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:45 +#, fuzzy +msgid "Storage Sphere" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:46 +msgid "Tank with Fixed Roof" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:47 +msgid "Tank with Floating Roof" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:48 +msgid "Tray Column, detailed" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:49 +msgid "Tray Column, simple" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:50 +#, fuzzy +msgid "Valve" +msgstr ":" + +#: sheets/ChemEng.sheet.in.h:51 +#, fuzzy +msgid "Valve, vertical" +msgstr "¿ ٲ" + +#: sheets/ChemEng.sheet.in.h:52 +msgid "Water Cooler" +msgstr "" + +#: sheets/ChemEng.sheet.in.h:53 +msgid "Water Cooler, vertical" +msgstr "" + +#: sheets/Circuit.sheet.in.h:1 +msgid "Circuit" +msgstr "" + +#: sheets/Circuit.sheet.in.h:2 +msgid "Components for circuit diagrams" +msgstr "" + +#: sheets/Circuit.sheet.in.h:3 +#, fuzzy +msgid "Ground point" +msgstr "ε巯 " + +#: sheets/Circuit.sheet.in.h:4 +#, fuzzy +msgid "Horizontal jumper" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:5 +#, fuzzy +msgid "Horizontally aligned LED" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:6 +#, fuzzy +msgid "Horizontally aligned capacitor" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:7 +#, fuzzy +msgid "Horizontally aligned diode" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:8 +#, fuzzy +msgid "Horizontally aligned fuse" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:9 +#, fuzzy +msgid "Horizontally aligned inductor" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:10 +#, fuzzy +msgid "Horizontally aligned inductor (European)" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:11 +#, fuzzy +msgid "Horizontally aligned powersource" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:12 +#, fuzzy +msgid "Horizontally aligned resistor" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:13 +#, fuzzy +msgid "Horizontally aligned resistor (European)" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:14 +#, fuzzy +msgid "Horizontally aligned zener diode" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:15 +#, fuzzy +msgid "Lamp" +msgstr "̸:" + +#: sheets/Circuit.sheet.in.h:16 sheets/ciscomisc.sheet.in.h:26 +msgid "Microphone" +msgstr "" + +#: sheets/Circuit.sheet.in.h:17 +#, fuzzy +msgid "NMOS transistor" +msgstr "" + +#: sheets/Circuit.sheet.in.h:18 +#, fuzzy +msgid "NPN bipolar transistor" +msgstr "" + +#: sheets/Circuit.sheet.in.h:19 +#, fuzzy +msgid "Operational amplifier" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:20 +#, fuzzy +msgid "PMOS transistor" +msgstr "" + +#: sheets/Circuit.sheet.in.h:21 +#, fuzzy +msgid "PNP bipolar transistor" +msgstr "" + +#: sheets/Circuit.sheet.in.h:22 sheets/ciscomisc.sheet.in.h:39 +msgid "Speaker" +msgstr "" + +#: sheets/Circuit.sheet.in.h:23 +#, fuzzy +msgid "Vertically aligned LED" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:24 +#, fuzzy +msgid "Vertically aligned capacitor" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:25 +#, fuzzy +msgid "Vertically aligned diode" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:26 +#, fuzzy +msgid "Vertically aligned fuse" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:27 +#, fuzzy +msgid "Vertically aligned inductor" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:28 +#, fuzzy +msgid "Vertically aligned inductor (European)" +msgstr " (_H)" + +#: sheets/Circuit.sheet.in.h:29 +#, fuzzy +msgid "Vertically aligned powersource" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:30 +#, fuzzy +msgid "Vertically aligned resistor" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:31 +#, fuzzy +msgid "Vertically aligned resistor (European)" +msgstr "Ʒ ٲ" + +#: sheets/Circuit.sheet.in.h:32 +#, fuzzy +msgid "Vertically aligned zener diode" +msgstr "Ʒ ٲ" + +#: sheets/Contact.sheet.in.h:1 +msgid "'if not' (normally closed) ladder contact" +msgstr "" + +#: sheets/Contact.sheet.in.h:2 +msgid "'if' (normally open) ladder contact" +msgstr "" + +#: sheets/Contact.sheet.in.h:3 +#, fuzzy +msgid "'jump' output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:4 +#, fuzzy +msgid "'reset' output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:5 +#, fuzzy +msgid "'set' output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:6 +msgid "Components for LADDER circuits" +msgstr "" + +#: sheets/Contact.sheet.in.h:7 +#, fuzzy +msgid "Ladder" +msgstr "" + +#: sheets/Contact.sheet.in.h:8 +#, fuzzy +msgid "Negative output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:9 +#, fuzzy +msgid "Power-saved 'reset' output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:10 +#, fuzzy +msgid "Power-saved 'set' output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:11 +#, fuzzy +msgid "Power-saved negative output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:12 +#, fuzzy +msgid "Power-saved simple output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:13 +#, fuzzy +msgid "Receptivity output variable" +msgstr " " + +#: sheets/Contact.sheet.in.h:14 +#, fuzzy +msgid "Simple output variable" +msgstr " " + +#: sheets/Cybernetics.sheet.in.h:1 +msgid "Constant factor below -1" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:2 +msgid "Constant factor between 0 and -1" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:3 +msgid "Constant factor between 0 and 1" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:4 +msgid "Constant factor greater 1" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:5 +msgid "Constant negative shift on the y-axis" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:6 +msgid "Constant positive shift on the y-axis" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:7 +msgid "Cybernetics" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:8 +msgid "Elements of cybernetic circuits" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:9 +msgid "Full wave rectifier (absolute value)" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:10 +msgid "Half wave rectifier or ramp input" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:11 +msgid "High pass filter" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:12 +msgid "Integrator - input bottom" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:13 +msgid "Integrator - input left" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:14 +msgid "Integrator - input right" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:15 +msgid "Integrator - input top" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:16 +msgid "Low pass filter" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:17 +msgid "Product" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:18 +msgid "Relay characteristic (sigma)" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:19 +msgid "Saturation characteristic" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:20 +msgid "Sensor - bottom" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:21 +msgid "Sensor - left" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:22 +msgid "Sensor - right" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:23 +msgid "Sensor - top" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:24 +msgid "Sigmoid characteristic" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:25 +msgid "Sine characteristic or input" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:26 +msgid "Sum" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:27 +msgid "Sum, subtracting bottom input" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:28 +msgid "Sum, subtracting left input" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:29 +msgid "Sum, subtracting right input" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:30 +msgid "Sum, subtracting top input" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:31 +msgid "Template for custom characteristics" +msgstr "" + +#: sheets/Cybernetics.sheet.in.h:32 +#, fuzzy +msgid "Time delay" +msgstr "Ӽ " + +#: sheets/EML.sheet.in.h:1 +msgid "EML" +msgstr "" + +#: sheets/EML.sheet.in.h:2 +msgid "Editor for EML Static Structure Diagrams." +msgstr "" + +#: sheets/EML.sheet.in.h:3 +msgid "Instantiation. One process create others" +msgstr "" + +#: sheets/EML.sheet.in.h:4 +msgid "Interaction between processes." +msgstr "" + +#: sheets/ER.sheet.in.h:2 +msgid "ER" +msgstr "" + +#: sheets/ER.sheet.in.h:3 +msgid "Editor for Entity Relations Diagrams" +msgstr "" + +#: sheets/ER.sheet.in.h:5 +#, fuzzy +msgid "Participation" +msgstr "" + +#: sheets/ER.sheet.in.h:7 +msgid "Weak entity" +msgstr "" + +#: sheets/Electric.sheet.in.h:1 +msgid "Components for electric circuits" +msgstr "" + +#: sheets/Electric.sheet.in.h:2 sheets/Pneumatic.sheet.in.h:9 +#, fuzzy +msgid "Connection point" +msgstr ":" + +#: sheets/Electric.sheet.in.h:3 +#, fuzzy +msgid "Electric" +msgstr "¿ ٲ" + +#: sheets/Electric.sheet.in.h:4 +msgid "Lamp or indicator light (horizontal)" +msgstr "" + +#: sheets/Electric.sheet.in.h:5 +msgid "Lamp or indicator light (vertical)" +msgstr "" + +#: sheets/Electric.sheet.in.h:6 +msgid "Normally closed contact (horizontal)" +msgstr "" + +#: sheets/Electric.sheet.in.h:7 +msgid "Normally closed contact (vertical)" +msgstr "" + +#: sheets/Electric.sheet.in.h:8 +msgid "Normally closed position switch (horizontal)" +msgstr "" + +#: sheets/Electric.sheet.in.h:9 +msgid "Normally closed position switch (vertical)" +msgstr "" + +#: sheets/Electric.sheet.in.h:10 +msgid "Normally open contact (horizontal)" +msgstr "" + +#: sheets/Electric.sheet.in.h:11 +msgid "Normally open contact (vertical)" +msgstr "" + +#: sheets/Electric.sheet.in.h:12 +msgid "Normally open position switch (horizontal)" +msgstr "" + +#: sheets/Electric.sheet.in.h:13 +msgid "Normally open position switch (vertical)" +msgstr "" + +#: sheets/Electric.sheet.in.h:14 +#, fuzzy +msgid "Relay (horizontal)" +msgstr "Ʒ ٲ" + +#: sheets/Electric.sheet.in.h:15 +#, fuzzy +msgid "Relay (vertical)" +msgstr "¿ ٲ" + +#: sheets/Electric.sheet.in.h:16 +msgid "The command organ of a relay (horizontal)" +msgstr "" + +#: sheets/Electric.sheet.in.h:17 +msgid "The command organ of a relay (vertical)" +msgstr "" + +#: sheets/FS.sheet.in.h:1 +msgid "Editor for Function Structure Diagrams." +msgstr "" + +#: sheets/FS.sheet.in.h:2 +msgid "FS" +msgstr "" + +#: sheets/FS.sheet.in.h:3 +#, fuzzy +msgid "Flow" +msgstr "帧 ü" + +#: sheets/FS.sheet.in.h:4 +#, fuzzy +msgid "Function" +msgstr ":" + +#: sheets/FS.sheet.in.h:5 +msgid "Orthogonal polyline flow" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:1 +#, fuzzy +msgid "Collate" +msgstr "ȣ" + +#: sheets/Flowchart.sheet.in.h:2 +#, fuzzy +msgid "Connector" +msgstr ":" + +#: sheets/Flowchart.sheet.in.h:3 sheets/SDL.sheet.in.h:4 +#, fuzzy +msgid "Decision" +msgstr ":" + +#: sheets/Flowchart.sheet.in.h:4 +msgid "Delay" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:6 +#, fuzzy +msgid "Document" +msgstr "۲ ũ:" + +#: sheets/Flowchart.sheet.in.h:8 +#, fuzzy +msgid "Flowchart" +msgstr "帧 ü" + +#: sheets/Flowchart.sheet.in.h:9 +msgid "Input/Output" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:10 +#, fuzzy +msgid "Internal storage" +msgstr " ̽" + +#: sheets/Flowchart.sheet.in.h:11 +msgid "Magnetic disk" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:12 +msgid "Magnetic drum" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:13 +msgid "Magnetic tape" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:14 +#, fuzzy +msgid "Manual input" +msgstr "۵" + +#: sheets/Flowchart.sheet.in.h:15 +#, fuzzy +msgid "Manual operation" +msgstr "۵" + +#: sheets/Flowchart.sheet.in.h:16 +msgid "Merge" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:17 +msgid "Objects to draw flowcharts" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:18 +msgid "Off page connector" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:19 +#, fuzzy +msgid "Offline storage" +msgstr " ̽" + +#: sheets/Flowchart.sheet.in.h:20 +msgid "Or" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:21 +#, fuzzy +msgid "Predefined process" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:22 +#, fuzzy +msgid "Preparation" +msgstr "۵" + +#: sheets/Flowchart.sheet.in.h:23 +#, fuzzy +msgid "Process/Auxiliary Operation" +msgstr "۵ Ⱥ" + +#: sheets/Flowchart.sheet.in.h:24 +msgid "Punched card" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:25 +msgid "Punched tape" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:26 +#, fuzzy +msgid "Sort" +msgstr "ȭǥ " + +#: sheets/Flowchart.sheet.in.h:27 +msgid "Summing junction" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:28 +msgid "Terminal Interrupt" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:29 +#, fuzzy +msgid "Transaction file" +msgstr "" + +#: sheets/Flowchart.sheet.in.h:30 +msgid "Transmittal tape" +msgstr "" + +#: sheets/GRAFCET.sheet.in.h:1 +#, fuzzy +msgid "AND vergent" +msgstr "κ ߰" + +#: sheets/GRAFCET.sheet.in.h:2 +msgid "Action to associate to a step" +msgstr "" + +#: sheets/GRAFCET.sheet.in.h:3 +msgid "Arc (upward)" +msgstr "" + +#: sheets/GRAFCET.sheet.in.h:4 +msgid "Condition (of an action)" +msgstr "" + +#: sheets/GRAFCET.sheet.in.h:5 +msgid "GRAFCET" +msgstr "" + +#: sheets/GRAFCET.sheet.in.h:10 +#, fuzzy +msgid "Macro sub-program call step" +msgstr "ø" + +#: sheets/GRAFCET.sheet.in.h:11 +#, fuzzy +msgid "OR vergent" +msgstr "κ ߰" + +#: sheets/GRAFCET.sheet.in.h:12 +msgid "Objects to design GRAFCET charts" +msgstr "" + +#: sheets/GRAFCET.sheet.in.h:14 +#, fuzzy +msgid "Transition" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:1 +msgid "Block 1, 2:4" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:2 +msgid "Block 2, 2:8" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:3 +msgid "Block 3, 4:4" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:4 +msgid "Block 4, 4:8" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:5 +msgid "Block 5, 3:3" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:6 +msgid "Block 6, 4:6" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:7 +#, fuzzy +msgid "Block 7" +msgstr "ϱ" + +#: sheets/IsometricMap.sheet.in.h:8 +msgid "Car 1, Front View" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:9 +msgid "Car 2, Rear View" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:10 +#, fuzzy +msgid "Corner 1" +msgstr "ڸ ϱ" + +#: sheets/IsometricMap.sheet.in.h:11 +#, fuzzy +msgid "Corner 2" +msgstr "ڸ ϱ" + +#: sheets/IsometricMap.sheet.in.h:12 +#, fuzzy +msgid "Crossroads" +msgstr "Ŭ" + +#: sheets/IsometricMap.sheet.in.h:13 +msgid "Elevated Road" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:14 +msgid "Factory" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:15 +msgid "Footbridge, Pedestrian Bridge" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:16 +msgid "Isometric Directional Map Shapes" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:17 +msgid "Long Straight Road Section" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:18 +msgid "Map, Isometric" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:19 +msgid "One Way Road Sign" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:20 +#, fuzzy +msgid "River" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:21 +#, fuzzy +msgid "Road Section" +msgstr ":" + +#: sheets/IsometricMap.sheet.in.h:22 +msgid "Roof1" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:23 +#, fuzzy +msgid "T-Junction" +msgstr ":" + +#: sheets/IsometricMap.sheet.in.h:24 +msgid "Train 1, angled downward" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:25 +msgid "Train 2, angled upward" +msgstr "" + +#: sheets/IsometricMap.sheet.in.h:26 +msgid "Tree 1" +msgstr "" + +#: sheets/Istar.sheet.in.h:1 +#, fuzzy +msgid "An i* agent" +msgstr "κ ߰" + +#: sheets/Istar.sheet.in.h:2 +msgid "An i* decomposition link" +msgstr "" + +#: sheets/Istar.sheet.in.h:3 +#, fuzzy +msgid "An i* dependency link" +msgstr " " + +#: sheets/Istar.sheet.in.h:4 +#, fuzzy +msgid "An i* goal" +msgstr "" + +#: sheets/Istar.sheet.in.h:5 +msgid "An i* means-ends link" +msgstr "" + +#: sheets/Istar.sheet.in.h:6 +#, fuzzy +msgid "An i* negative contribution" +msgstr " " + +#: sheets/Istar.sheet.in.h:7 +#, fuzzy +msgid "An i* position" +msgstr "ռ" + +#: sheets/Istar.sheet.in.h:8 +msgid "An i* positive contribution" +msgstr "" + +#: sheets/Istar.sheet.in.h:9 +msgid "An i* resource" +msgstr "" + +#: sheets/Istar.sheet.in.h:10 +#, fuzzy +msgid "An i* role" +msgstr "" + +#: sheets/Istar.sheet.in.h:11 +msgid "An i* softgoal" +msgstr "" + +#: sheets/Istar.sheet.in.h:12 +msgid "An i* task" +msgstr "" + +#: sheets/Istar.sheet.in.h:13 +msgid "An i* unspecified actor" +msgstr "" + +#: sheets/Istar.sheet.in.h:14 +msgid "An i* unspecified link" +msgstr "" + +#: sheets/Istar.sheet.in.h:15 +msgid "Objects to design i* diagrams" +msgstr "" + +#: sheets/Istar.sheet.in.h:16 +msgid "RE-i*" +msgstr "" + +#: sheets/Jackson.sheet.in.h:1 +msgid "A Jackson designed domain" +msgstr "" + +#: sheets/Jackson.sheet.in.h:2 +msgid "A Jackson given domain" +msgstr "" + +#: sheets/Jackson.sheet.in.h:3 +msgid "A Jackson machine domain" +msgstr "" + +#: sheets/Jackson.sheet.in.h:4 +msgid "A Jackson requirement" +msgstr "" + +#: sheets/Jackson.sheet.in.h:5 +msgid "A Jackson requirement phenomenon" +msgstr "" + +#: sheets/Jackson.sheet.in.h:6 +msgid "A Jackson shared phenomenon" +msgstr "" + +#: sheets/Jackson.sheet.in.h:7 +msgid "Objects to design Jacskon diagrams" +msgstr "" + +#: sheets/Jackson.sheet.in.h:8 +msgid "RE-Jackson" +msgstr "" + +#: sheets/KAOS.sheet.in.h:1 +msgid "A KAOS AND refinement" +msgstr "" + +#: sheets/KAOS.sheet.in.h:2 +msgid "A KAOS OR refinement" +msgstr "" + +#: sheets/KAOS.sheet.in.h:3 +#, fuzzy +msgid "A KAOS agent" +msgstr "κ ߰" + +#: sheets/KAOS.sheet.in.h:4 +msgid "A KAOS assumption" +msgstr "" + +#: sheets/KAOS.sheet.in.h:5 +msgid "A KAOS binary conflict" +msgstr "" + +#: sheets/KAOS.sheet.in.h:6 +msgid "A KAOS capable-of" +msgstr "" + +#: sheets/KAOS.sheet.in.h:7 +msgid "A KAOS complete AND refinement" +msgstr "" + +#: sheets/KAOS.sheet.in.h:8 +msgid "A KAOS complete OR refinement" +msgstr "" + +#: sheets/KAOS.sheet.in.h:9 +msgid "A KAOS contribution" +msgstr "" + +#: sheets/KAOS.sheet.in.h:10 +msgid "A KAOS control link" +msgstr "" + +#: sheets/KAOS.sheet.in.h:11 +msgid "A KAOS goal" +msgstr "" + +#: sheets/KAOS.sheet.in.h:12 +msgid "A KAOS input" +msgstr "" + +#: sheets/KAOS.sheet.in.h:13 +#, fuzzy +msgid "A KAOS monitor link" +msgstr "ռ" + +#: sheets/KAOS.sheet.in.h:14 +msgid "A KAOS obstacle" +msgstr "" + +#: sheets/KAOS.sheet.in.h:15 +msgid "A KAOS obstruction" +msgstr "" + +#: sheets/KAOS.sheet.in.h:16 +#, fuzzy +msgid "A KAOS operation" +msgstr "۵" + +#: sheets/KAOS.sheet.in.h:17 +#, fuzzy +msgid "A KAOS operationalization" +msgstr " (_H)" + +#: sheets/KAOS.sheet.in.h:18 +msgid "A KAOS output" +msgstr "" + +#: sheets/KAOS.sheet.in.h:19 +msgid "A KAOS performs" +msgstr "" + +#: sheets/KAOS.sheet.in.h:20 +#, fuzzy +msgid "A KAOS requirement" +msgstr "κ ߰" + +#: sheets/KAOS.sheet.in.h:21 +msgid "A KAOS responsibility" +msgstr "" + +#: sheets/KAOS.sheet.in.h:22 +msgid "A KAOS softgoal" +msgstr "" + +#: sheets/KAOS.sheet.in.h:23 +msgid "Objects to design KAOS diagrams" +msgstr "" + +#: sheets/KAOS.sheet.in.h:24 +msgid "RE-KAOS" +msgstr "" + +#: sheets/Logic.sheet.in.h:1 +#, fuzzy +msgid "AND gate" +msgstr "" + +#: sheets/Logic.sheet.in.h:2 +msgid "Boolean Logic" +msgstr "" + +#: sheets/Logic.sheet.in.h:3 +msgid "Crossconnector" +msgstr "" + +#: sheets/Logic.sheet.in.h:4 +#, fuzzy +msgid "Inverter" +msgstr " ̽" + +#: sheets/Logic.sheet.in.h:5 +msgid "Logic" +msgstr "" + +#: sheets/Logic.sheet.in.h:6 +#, fuzzy +msgid "NAND gate" +msgstr "" + +#: sheets/Logic.sheet.in.h:7 +#, fuzzy +msgid "NOR gate" +msgstr "" + +#: sheets/Logic.sheet.in.h:8 +msgid "NOT" +msgstr "" + +#: sheets/Logic.sheet.in.h:9 +#, fuzzy +msgid "OR gate" +msgstr "" + +#: sheets/Logic.sheet.in.h:10 +#, fuzzy +msgid "Simple buffer" +msgstr " " + +#: sheets/Logic.sheet.in.h:11 +#, fuzzy +msgid "XOR gate" +msgstr "" + +#: sheets/MSE.sheet.in.h:1 +#, fuzzy +msgid "Demultiplexer" +msgstr "ߺ" + +#: sheets/MSE.sheet.in.h:2 +#, fuzzy +msgid "Large extension node" +msgstr "Ȯ" + +#: sheets/MSE.sheet.in.h:3 +msgid "MSE" +msgstr "" + +#: sheets/MSE.sheet.in.h:4 +#, fuzzy +msgid "Multiplexer" +msgstr "ߺ" + +#: sheets/MSE.sheet.in.h:5 +#, fuzzy +msgid "Node center" +msgstr "߰" + +#: sheets/MSE.sheet.in.h:6 +#, fuzzy +msgid "Small extension node" +msgstr "Ȯ" + +#: sheets/MSE.sheet.in.h:7 +msgid "Tactical satellite communications terminal" +msgstr "" + +#: sheets/MSE.sheet.in.h:8 +msgid "U.S. Army Mobile Subscriber Equipment Components" +msgstr "" + +#: sheets/Misc.sheet.in.h:2 +#, fuzzy +msgid "Folder" +msgstr "" + +#: sheets/Misc.sheet.in.h:3 +msgid "Misc" +msgstr "" + +#: sheets/Misc.sheet.in.h:4 +msgid "Miscellaneous Shapes" +msgstr "" + +#: sheets/Misc.sheet.in.h:5 +#, fuzzy +msgid "Traditional clock" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:1 +#, fuzzy +msgid "2/2 distributor" +msgstr "Ӽ" + +#: sheets/Pneumatic.sheet.in.h:2 +#, fuzzy +msgid "3/2 distributor" +msgstr "Ӽ" + +#: sheets/Pneumatic.sheet.in.h:3 +#, fuzzy +msgid "3/3 distributor" +msgstr "Ӽ" + +#: sheets/Pneumatic.sheet.in.h:4 +#, fuzzy +msgid "4/2 distributor" +msgstr "Ӽ" + +#: sheets/Pneumatic.sheet.in.h:5 +#, fuzzy +msgid "5/2 distributor" +msgstr "Ӽ" + +#: sheets/Pneumatic.sheet.in.h:6 +#, fuzzy +msgid "5/3 distributor" +msgstr "Ӽ" + +#: sheets/Pneumatic.sheet.in.h:7 +msgid "Air exhaust orifice" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:8 +msgid "Components for pneumatic and hydraulic circuits" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:10 +msgid "Double-effect jack" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:11 +msgid "Electric command (double coil)" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:12 +msgid "Electric command (single coil)" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:13 +msgid "Generic pressure source" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:14 +msgid "Hydraulic pressure source" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:15 +msgid "Indirect command by hydraulic driver" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:16 +msgid "Indirect command by pneumatic driver" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:17 +msgid "Mechanical command by spring" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:18 +msgid "Mechanical command by tappet" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:19 +msgid "Muscular command" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:20 +msgid "Normally-in simple-effect jack" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:21 +msgid "Normally-out simple-effect jack" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:22 +msgid "Pneumatic pressure source" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:23 +msgid "Pneumatic/Hydraulic" +msgstr "" + +#: sheets/Pneumatic.sheet.in.h:24 +msgid "Push-button command" +msgstr "" + +#: sheets/SADT.sheet.in.h:1 +msgid "Activity/data box" +msgstr "" + +#: sheets/SADT.sheet.in.h:2 +msgid "Activity/data flow arrow" +msgstr "" + +#: sheets/SADT.sheet.in.h:3 +msgid "Flow label" +msgstr "" + +#: sheets/SADT.sheet.in.h:4 +msgid "Objects to design SADT diagrams" +msgstr "" + +#: sheets/SADT.sheet.in.h:5 +msgid "SADT/IDEF0" +msgstr "" + +#: sheets/SDL.sheet.in.h:1 +msgid "Action being executed" +msgstr "" + +#: sheets/SDL.sheet.in.h:2 +#, fuzzy +msgid "Block type reference" +msgstr "" + +#: sheets/SDL.sheet.in.h:5 +msgid "Function call" +msgstr "" + +#: sheets/SDL.sheet.in.h:6 +#, fuzzy +msgid "Function header" +msgstr ":" + +#: sheets/SDL.sheet.in.h:7 +msgid "Generic text note" +msgstr "" + +#: sheets/SDL.sheet.in.h:8 +msgid "In/Out connector" +msgstr "" + +#: sheets/SDL.sheet.in.h:10 +msgid "Procedure return" +msgstr "" + +#: sheets/SDL.sheet.in.h:11 +#, fuzzy +msgid "Process type reference" +msgstr "" + +#: sheets/SDL.sheet.in.h:12 +#, fuzzy +msgid "Receive message" +msgstr "" + +#: sheets/SDL.sheet.in.h:13 +msgid "SDL" +msgstr "" + +#: sheets/SDL.sheet.in.h:14 +#, fuzzy +msgid "Save state" +msgstr "ȭǥ " + +#: sheets/SDL.sheet.in.h:15 +#, fuzzy +msgid "Send message" +msgstr "" + +#: sheets/SDL.sheet.in.h:16 +#, fuzzy +msgid "Service type reference" +msgstr "" + +#: sheets/SDL.sheet.in.h:17 +msgid "Specification and Description Language." +msgstr "" + +#: sheets/SDL.sheet.in.h:18 sheets/UML.sheet.in.h:28 +#, fuzzy +msgid "State" +msgstr "ȭǥ " + +#: sheets/UML.sheet.in.h:1 +msgid "Activity" +msgstr "" + +#: sheets/UML.sheet.in.h:3 +msgid "Aggregation, one class is part of another" +msgstr "" + +#: sheets/UML.sheet.in.h:4 +msgid "Association, two classes are associated" +msgstr "" + +#: sheets/UML.sheet.in.h:7 +#, fuzzy +msgid "Class stereotype icon" +msgstr "ø Ŭ" + +#: sheets/UML.sheet.in.h:8 +#, fuzzy +msgid "Component" +msgstr "߰" + +#: sheets/UML.sheet.in.h:9 +msgid "Constraint, place a constraint on something" +msgstr "" + +#: sheets/UML.sheet.in.h:11 +msgid "Editor for UML Static Structure Diagrams" +msgstr "" + +#: sheets/UML.sheet.in.h:15 +#, fuzzy +msgid "Fork/union" +msgstr ":" + +#: sheets/UML.sheet.in.h:16 +msgid "Generalization, class inheritance" +msgstr "" + +#: sheets/UML.sheet.in.h:17 +msgid "Implements, class implements a specific interface" +msgstr "" + +#: sheets/UML.sheet.in.h:18 +#, fuzzy +msgid "Initial/end state" +msgstr " " + +#: sheets/UML.sheet.in.h:19 +#, fuzzy +msgid "Large package" +msgstr "׸ " + +#: sheets/UML.sheet.in.h:20 +#, fuzzy +msgid "Lifeline" +msgstr "" + +#: sheets/UML.sheet.in.h:21 +#, fuzzy +msgid "Message" +msgstr "" + +#: sheets/UML.sheet.in.h:22 +#, fuzzy +msgid "Node" +msgstr "ƴϿ" + +#: sheets/UML.sheet.in.h:23 +#, fuzzy +msgid "Note" +msgstr "" + +#: sheets/UML.sheet.in.h:24 +#, fuzzy +msgid "Object" +msgstr "/ü(_O)" + +#: sheets/UML.sheet.in.h:25 +msgid "Realizes, implements a specific interface" +msgstr "" + +#: sheets/UML.sheet.in.h:27 +#, fuzzy +msgid "Small package" +msgstr "׸ " + +#: sheets/UML.sheet.in.h:30 +msgid "UML" +msgstr "" + +#: sheets/UML.sheet.in.h:31 +msgid "Use case" +msgstr "" + +#: sheets/chronogram.sheet.in.h:1 +msgid "Chronogram" +msgstr "" + +#: sheets/chronogram.sheet.in.h:2 +#, fuzzy +msgid "Data line" +msgstr " " + +#: sheets/chronogram.sheet.in.h:3 +msgid "Objects to design chronogram charts" +msgstr "" + +#: sheets/chronogram.sheet.in.h:4 +#, fuzzy +msgid "Time scale" +msgstr "Ӽ " + +#: sheets/ciscocomputer.sheet.in.h:1 +#, fuzzy +msgid "Cisco - Computer" +msgstr "ڸ ϱ" + +#: sheets/ciscocomputer.sheet.in.h:2 +msgid "Cisco CA" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:3 +msgid "CiscoSecurity" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:4 +msgid "CiscoWorks workstation" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:5 +msgid "Communications server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:6 +msgid "Computer shapes by Cisco" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:7 +msgid "Directory Server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:8 +#, fuzzy +msgid "File Server" +msgstr " ̸:" + +#: sheets/ciscocomputer.sheet.in.h:9 +msgid "HP Mini" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:10 +msgid "Handheld" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:11 +msgid "Host" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:12 +msgid "IBM Mini (AS400)" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:13 +msgid "IBM Tower" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:14 +msgid "IBM mainframe" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:15 +msgid "IP Softphone" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:16 +msgid "IPTV broadcast server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:17 +msgid "Laptop" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:18 +#, fuzzy +msgid "Macintosh" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:19 +msgid "MicroWebserver" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:20 +msgid "Mini VAX" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:21 +msgid "MoH server (Music on Hold)" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:22 +msgid "PC" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:23 +msgid "PC Video" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:24 +msgid "PDA" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:26 +#, fuzzy +msgid "Relational Database" +msgstr "۵ " + +#: sheets/ciscocomputer.sheet.in.h:27 +msgid "SC2200/VSC3000 host" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:28 +msgid "SIP Proxy server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:29 +msgid "STB (set top box)" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:30 +#, fuzzy +msgid "SUN workstation" +msgstr ":" + +#: sheets/ciscocomputer.sheet.in.h:31 +#, fuzzy +msgid "Scanner" +msgstr ":" + +#: sheets/ciscocomputer.sheet.in.h:32 +msgid "Server with PC Router" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:33 +#, fuzzy +msgid "Softphone" +msgstr "ε巯 " + +#: sheets/ciscocomputer.sheet.in.h:34 +msgid "Software based server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:35 +#, fuzzy +msgid "Storage array" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:36 +#, fuzzy +msgid "Supercomputer" +msgstr "ڸ ϱ" + +#: sheets/ciscocomputer.sheet.in.h:37 +msgid "TV" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:38 +msgid "Terminal" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:39 +#, fuzzy +msgid "Turret" +msgstr "߰" + +#: sheets/ciscocomputer.sheet.in.h:40 +msgid "Unity server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:41 +msgid "Voice commserver" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:42 +msgid "WWW server" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:43 +msgid "Web browser" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:44 +msgid "Web cluster" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:45 +msgid "Wireless" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:46 +msgid "Workgroup director" +msgstr "" + +#: sheets/ciscocomputer.sheet.in.h:47 +#, fuzzy +msgid "Workstation" +msgstr ":" + +#: sheets/ciscohub.sheet.in.h:1 +msgid "100BaseT hub" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:2 +msgid "ATM fast gigabit etherswitch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:3 +msgid "ATM switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:4 +msgid "Cisco - Hub" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:5 +msgid "Cisco hub" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:6 +msgid "Class 4/5 switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:7 +msgid "Content service switch 1100" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:8 +msgid "Content switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:9 +msgid "Content switch module" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:10 +msgid "Generic softswitch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:11 +msgid "Hub" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:12 +msgid "Hub and switch shapes by Cisco" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:13 +msgid "ISDN switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:14 +#, fuzzy +msgid "Layer 3 switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:15 +msgid "MGX 8220 switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:16 +msgid "MGX 8240 switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:17 +msgid "MGX 8260 switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:18 +msgid "MultiSwitch device" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:19 +#, fuzzy +msgid "Multilayer switch" +msgstr "ߺ" + +#: sheets/ciscohub.sheet.in.h:20 +msgid "PBX switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:21 +msgid "Programmable switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:22 +msgid "Route switch processor" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:23 +msgid "Small hub" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:24 +msgid "Voice ATM switch" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:25 +#, fuzzy +msgid "Voice switch" +msgstr " " + +#: sheets/ciscohub.sheet.in.h:26 +msgid "Voice switch 2" +msgstr "" + +#: sheets/ciscohub.sheet.in.h:27 +msgid "Workgroup switch" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:1 +msgid "BBFW" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:2 +msgid "BBFW media" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:3 +msgid "Branch office" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:4 +#, fuzzy +msgid "Breakout box" +msgstr " " + +#: sheets/ciscomisc.sheet.in.h:5 +#, fuzzy +msgid "Car" +msgstr "Ŭ" + +#: sheets/ciscomisc.sheet.in.h:6 +msgid "Cellular phone" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:7 +msgid "Cisco - Misc" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:8 +#, fuzzy +msgid "Diskette" +msgstr "Ӽ" + +#: sheets/ciscomisc.sheet.in.h:9 +#, fuzzy +msgid "Dot-Dot" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:10 +msgid "End office" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:11 +msgid "Fax" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:12 +#, fuzzy +msgid "File cabinet" +msgstr " ̸:" + +#: sheets/ciscomisc.sheet.in.h:13 +msgid "Generic building" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:14 +msgid "Government building" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:15 +msgid "H.323" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:16 +msgid "HootPhone" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:17 +msgid "IP phone" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:18 +msgid "ITP" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:19 +msgid "Key" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:20 +msgid "LAN to LAN" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:22 +msgid "MAU" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:23 +msgid "MDU" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:24 +msgid "Mac woman" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:25 +msgid "Man/Woman" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:27 +msgid "Miscellaneous shapes by Cisco" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:28 +msgid "PC man" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:29 +#, fuzzy +msgid "Phone" +msgstr "ἱ" + +#: sheets/ciscomisc.sheet.in.h:30 +msgid "Phone/Fax" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:31 +msgid "RPS" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:32 +#, fuzzy +msgid "Radio tower" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:33 +msgid "Running man" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:34 +msgid "SVX (interchangeable with End office)" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:35 +#, fuzzy +msgid "Satellite" +msgstr ":" + +#: sheets/ciscomisc.sheet.in.h:36 +msgid "Satellite dish" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:37 +msgid "Sitting woman" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:38 +msgid "Small business" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:40 +msgid "Standing man" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:41 +#, fuzzy +msgid "Tablet" +msgstr "ڿ" + +#: sheets/ciscomisc.sheet.in.h:42 +msgid "Telecommuter" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:43 +msgid "Telecommuter house" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:44 +msgid "Telecommuter house/router" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:45 +msgid "TokenRing" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:46 +msgid "Truck" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:47 +msgid "UPS" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:48 +msgid "University" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:49 +msgid "Video camera" +msgstr "" + +#: sheets/ciscomisc.sheet.in.h:50 +msgid "Wireless transport" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:1 +msgid "10700" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:2 +msgid "15200" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:3 +msgid "3174 (desktop) cluster controller" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:4 +msgid "3X74 (floor) cluster controller" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:5 +msgid "6701" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:6 +msgid "6705 Integrated access device" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:7 +msgid "6732 Multiservice access platform" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:8 +msgid "ADM" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:9 +#, fuzzy +msgid "ASIC processor" +msgstr "ȣ " + +#: sheets/cisconetwork.sheet.in.h:10 +msgid "ATA" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:11 +msgid "ATM 3800" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:12 +msgid "AccessPoint" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:13 +msgid "BBSM" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:14 +msgid "BTS 10200" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:15 +#, fuzzy +msgid "Bridge" +msgstr ":" + +#: sheets/cisconetwork.sheet.in.h:16 +msgid "CDDI-FDDI" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:17 +msgid "CDM Content distribution manager" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:18 +msgid "CSU/DSU" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:19 +msgid "Cable modem" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:20 +#, fuzzy +msgid "CallManager" +msgstr "ȣ" + +#: sheets/cisconetwork.sheet.in.h:21 +msgid "Catalyst access gateway" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:22 +msgid "Centri firewall" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:23 +msgid "Cisco - Network" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:24 +#, fuzzy +msgid "Cloud" +msgstr "ݱ" + +#: sheets/cisconetwork.sheet.in.h:25 +msgid "Content engine (Cache director)" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:26 +msgid "Content transformation engine (CTE)" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:27 +msgid "DPT" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:28 +msgid "DSLAM" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:29 +#, fuzzy +msgid "DWDM filter" +msgstr "ǻ ׷ Ÿ " + +#: sheets/cisconetwork.sheet.in.h:30 +#, fuzzy +msgid "Distributed director" +msgstr "Ӽ" + +#: sheets/cisconetwork.sheet.in.h:31 +#, fuzzy +msgid "FC storage" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:32 +msgid "FDDI ring" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:33 +#, fuzzy +msgid "Firewall" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:34 +#, fuzzy +msgid "Front end processor" +msgstr "ȣ " + +#: sheets/cisconetwork.sheet.in.h:35 +msgid "General appliance" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:36 +msgid "Generic gateway" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:37 +#, fuzzy +msgid "Generic processor" +msgstr "ȣ " + +#: sheets/cisconetwork.sheet.in.h:38 +msgid "ICM" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:39 +msgid "ICS" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:40 +msgid "IOS firewall" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:41 +msgid "IP" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:42 +msgid "IP DSL" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:43 +msgid "IPTC" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:44 +msgid "IPTV content manager" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:45 +msgid "LocalDirector" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:46 +msgid "LongReach CPE" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:47 +msgid "MAS gateway" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:48 +msgid "ME 1100" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:49 +msgid "MUX" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:50 +msgid "Metro 1500" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:51 +#, fuzzy +msgid "Modem" +msgstr " ̵" + +#: sheets/cisconetwork.sheet.in.h:52 +msgid "NetRanger intrusion detection system" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:53 +msgid "NetSonar security scanner" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:54 +#, fuzzy +msgid "Network management" +msgstr "Ʈ ǥ ü" + +#: sheets/cisconetwork.sheet.in.h:55 +msgid "Network shapes by Cisco" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:56 +msgid "ONS15500 DWDM platform" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:57 +msgid "Octel" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:58 +#, fuzzy +msgid "Optical amplifier" +msgstr " (_H)" + +#: sheets/cisconetwork.sheet.in.h:59 +msgid "Optical transport" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:60 +msgid "PAD" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:61 +msgid "PAD X.28" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:62 +msgid "PBX" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:63 +msgid "PC adapter card" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:64 +msgid "PC router card" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:65 +msgid "PIX firewall" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:66 +msgid "PXF" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:67 +msgid "Protocol translator" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:68 +#, fuzzy +msgid "RateMUX" +msgstr ":" + +#: sheets/cisconetwork.sheet.in.h:69 +#, fuzzy +msgid "Repeater" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:70 +msgid "SC2200 (Signalling controller)" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:71 +msgid "STP" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:72 +#, fuzzy +msgid "System controller" +msgstr "Ī " + +#: sheets/cisconetwork.sheet.in.h:73 +#, fuzzy +msgid "Transpath" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:74 +msgid "Universal gateway" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:75 +msgid "VIP" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:76 +msgid "VPN concentrator" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:77 +msgid "VPN gateway" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:78 +msgid "Virtual switch controller (VSC 3000)" +msgstr "" + +#: sheets/cisconetwork.sheet.in.h:79 +msgid "Wireless bridge" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:1 +msgid "7500ARS (7513)" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:2 +msgid "7505" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:3 +msgid "7507" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:4 +#, fuzzy +msgid "ATM router" +msgstr "ڸ ϱ" + +#: sheets/ciscorouter.sheet.in.h:5 +msgid "ATM tag sw gigabit router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:6 +msgid "ATM tag switch router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:7 +msgid "Broadband router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:8 +msgid "Cisco - Router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:9 +msgid "Cisco 1000" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:10 +msgid "Content service router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:11 +msgid "Gigabit switch router (ATM tag)" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:12 +#, fuzzy +msgid "IAD router" +msgstr "ڸ ϱ" + +#: sheets/ciscorouter.sheet.in.h:13 +msgid "IP telephony router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:14 +msgid "NetFlow router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:15 +msgid "Optical services router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:16 +#, fuzzy +msgid "Router" +msgstr ":" + +#: sheets/ciscorouter.sheet.in.h:17 +msgid "Router in building" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:18 +msgid "Router shapes by Cisco" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:19 +msgid "Router with firewall" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:20 +msgid "Router with silicon switch" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:21 +#, fuzzy +msgid "Storage router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:22 +#, fuzzy +msgid "TDM router" +msgstr "ڸ ϱ" + +#: sheets/ciscorouter.sheet.in.h:23 +msgid "Voice router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:24 +msgid "Wavelength router" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:25 +msgid "Workgroup 5000 multilayer switch" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:26 +msgid "Workgroup 5002 multilayer switch" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:27 +msgid "Workgroup 5500 multilayer switch" +msgstr "" + +#: sheets/ciscorouter.sheet.in.h:28 +msgid "uBR910" +msgstr "" + +#: sheets/civil.sheet.in.h:1 +msgid "Aerator with bubbles" +msgstr "" + +#: sheets/civil.sheet.in.h:2 +msgid "Backflow preventer" +msgstr "" + +#: sheets/civil.sheet.in.h:3 +#, fuzzy +msgid "Basin" +msgstr "" + +#: sheets/civil.sheet.in.h:4 +#, fuzzy +msgid "Bivalent vertical rest" +msgstr "¿ ٲ" + +#: sheets/civil.sheet.in.h:5 +msgid "Civil" +msgstr "" + +#: sheets/civil.sheet.in.h:6 +msgid "Civil Engineering Components" +msgstr "" + +#: sheets/civil.sheet.in.h:7 +#, fuzzy +msgid "Container" +msgstr "ռ" + +#: sheets/civil.sheet.in.h:8 +msgid "Final-settling basin" +msgstr "" + +#: sheets/civil.sheet.in.h:9 +#, fuzzy +msgid "Frequency converter" +msgstr " ̽" + +#: sheets/civil.sheet.in.h:10 +msgid "Gas bottle" +msgstr "" + +#: sheets/civil.sheet.in.h:11 +#, fuzzy +msgid "Horizontal limiting line" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:12 +#, fuzzy +msgid "Horizontal rest" +msgstr "Ʒ ٲ" + +#: sheets/civil.sheet.in.h:13 +#, fuzzy +msgid "Horizontally aligned arrow" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:14 +#, fuzzy +msgid "Horizontally aligned compressor" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:15 +#, fuzzy +msgid "Horizontally aligned pump" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:16 +#, fuzzy +msgid "Horizontally aligned valve" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:17 +#, fuzzy +msgid "Motor" +msgstr "ռ" + +#: sheets/civil.sheet.in.h:18 +msgid "Preliminary clarification tank" +msgstr "" + +#: sheets/civil.sheet.in.h:19 +#, fuzzy +msgid "Reference line" +msgstr " " + +#: sheets/civil.sheet.in.h:20 +#, fuzzy +msgid "Rotor" +msgstr "ռ" + +#: sheets/civil.sheet.in.h:21 +#, fuzzy +msgid "Soil" +msgstr "Ǽ" + +#: sheets/civil.sheet.in.h:22 +#, fuzzy +msgid "Vertical limiting line" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:23 +#, fuzzy +msgid "Vertical rest" +msgstr "¿ ٲ" + +#: sheets/civil.sheet.in.h:24 +#, fuzzy +msgid "Vertically aligned arrow" +msgstr "Ʒ ٲ" + +#: sheets/civil.sheet.in.h:25 +#, fuzzy +msgid "Vertically aligned compressor" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:26 +#, fuzzy +msgid "Vertically aligned propeller" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:27 +#, fuzzy +msgid "Vertically aligned pump" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:28 +#, fuzzy +msgid "Vertically aligned valve" +msgstr " (_H)" + +#: sheets/civil.sheet.in.h:29 +msgid "Water level" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:1 +msgid "Jigsaw" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:2 +msgid "Jigsaw - part_iiii" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:3 +msgid "Jigsaw - part_iiio" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:4 +msgid "Jigsaw - part_iioi" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:5 +msgid "Jigsaw - part_iioo" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:6 +msgid "Jigsaw - part_ioii" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:7 +msgid "Jigsaw - part_ioio" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:8 +msgid "Jigsaw - part_iooi" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:9 +msgid "Jigsaw - part_iooo" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:10 +msgid "Jigsaw - part_oiii" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:11 +msgid "Jigsaw - part_oiio" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:12 +msgid "Jigsaw - part_oioi" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:13 +msgid "Jigsaw - part_oioo" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:14 +msgid "Jigsaw - part_ooii" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:15 +msgid "Jigsaw - part_ooio" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:16 +msgid "Jigsaw - part_oooi" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:17 +msgid "Jigsaw - part_oooo" +msgstr "" + +#: sheets/jigsaw.sheet.in.h:18 +msgid "Pieces of a jigsaw" +msgstr "" + +#: sheets/network.sheet.in.h:1 +msgid "24 Port Patch Panel" +msgstr "" + +#: sheets/network.sheet.in.h:2 +msgid "3 1/2 inch diskette" +msgstr "" + +#: sheets/network.sheet.in.h:3 +msgid "ATM switch symbol" +msgstr "" + +#: sheets/network.sheet.in.h:4 +msgid "Antenna for wireless transmission" +msgstr "" + +#: sheets/network.sheet.in.h:5 +#, fuzzy +msgid "Bigtower PC" +msgstr "ռ" + +#: sheets/network.sheet.in.h:6 +#, fuzzy +msgid "Computer" +msgstr "ڸ ϱ" + +#: sheets/network.sheet.in.h:7 +msgid "Desktop PC" +msgstr "" + +#: sheets/network.sheet.in.h:8 +msgid "Digitizing board" +msgstr "" + +#: sheets/network.sheet.in.h:9 +msgid "Ethernet bus" +msgstr "" + +#: sheets/network.sheet.in.h:10 +msgid "External DAT drive" +msgstr "" + +#: sheets/network.sheet.in.h:11 +#, fuzzy +msgid "Firewall router" +msgstr " :" + +#: sheets/network.sheet.in.h:12 +msgid "Laptop PC" +msgstr "" + +#: sheets/network.sheet.in.h:13 +#, fuzzy +msgid "Miditower PC" +msgstr "ռ" + +#: sheets/network.sheet.in.h:14 +#, fuzzy +msgid "Minitower PC" +msgstr "ռ" + +#: sheets/network.sheet.in.h:15 +msgid "Mobile phone" +msgstr "" + +#: sheets/network.sheet.in.h:16 +msgid "Mobile telephony base station" +msgstr "" + +#: sheets/network.sheet.in.h:17 +msgid "Mobile telephony cell" +msgstr "" + +#: sheets/network.sheet.in.h:18 +msgid "Modular switching system" +msgstr "" + +#: sheets/network.sheet.in.h:19 +#, fuzzy +msgid "Monitor" +msgstr "ռ" + +#: sheets/network.sheet.in.h:20 +msgid "Network" +msgstr "" + +#: sheets/network.sheet.in.h:21 +msgid "Network cloud" +msgstr "" + +#: sheets/network.sheet.in.h:22 +msgid "Objects to design network diagrams with" +msgstr "" + +#: sheets/network.sheet.in.h:23 +#, fuzzy +msgid "Plotter" +msgstr "ڸ ϱ" + +#: sheets/network.sheet.in.h:24 +msgid "RJ45 wall-plug" +msgstr "" + +#: sheets/network.sheet.in.h:25 +msgid "Router symbol" +msgstr "" + +#: sheets/network.sheet.in.h:26 +#, fuzzy +msgid "Simple modem" +msgstr " " + +#: sheets/network.sheet.in.h:27 +#, fuzzy +msgid "Simple printer" +msgstr " " + +#: sheets/network.sheet.in.h:28 +#, fuzzy +msgid "Speaker with integrated amplifier" +msgstr " (_H)" + +#: sheets/network.sheet.in.h:29 +#, fuzzy +msgid "Speaker without amplifier" +msgstr " (_H)" + +#: sheets/network.sheet.in.h:30 +msgid "Stackable hub or switch" +msgstr "" + +#: sheets/network.sheet.in.h:31 +#, fuzzy +msgid "Storage" +msgstr "" + +#: sheets/network.sheet.in.h:32 +msgid "Switch symbol" +msgstr "" + +#: sheets/network.sheet.in.h:33 +msgid "Telephone" +msgstr "" + +#: sheets/network.sheet.in.h:34 +msgid "UNIX workstation" +msgstr "" + +#: sheets/network.sheet.in.h:35 +#, fuzzy +msgid "WAN connection" +msgstr ":" + +#: sheets/network.sheet.in.h:36 +#, fuzzy +msgid "WAN link" +msgstr ":" + +#: sheets/network.sheet.in.h:37 +msgid "Wall-plug for the scEAD cabling system" +msgstr "" + +#: sheets/network.sheet.in.h:38 +msgid "Workstation monitor" +msgstr "" + +#: sheets/network.sheet.in.h:39 +msgid "ZIP disk" +msgstr "" + +#: sheets/sybase.sheet.in.h:1 +msgid "Log transfer manager or rep agent" +msgstr "" + +#: sheets/sybase.sheet.in.h:2 +msgid "Objects to design Sybase replication domain diagrams with" +msgstr "" + +#: sheets/sybase.sheet.in.h:3 +msgid "Replication server manager" +msgstr "" + +#: sheets/sybase.sheet.in.h:4 +msgid "Stable storage device" +msgstr "" + +#: sheets/sybase.sheet.in.h:5 +#, fuzzy +msgid "Sybase" +msgstr "" + +#: sheets/sybase.sheet.in.h:6 +msgid "Sybase client application" +msgstr "" + +#: sheets/sybase.sheet.in.h:7 +msgid "Sybase dataserver" +msgstr "" + +#: sheets/sybase.sheet.in.h:8 +msgid "Sybase replication server" +msgstr "" + +#~ msgid "Untitled-%d" +#~ msgstr "-%d" + +#, fuzzy +#~ msgid "Export file name to use" +#~ msgstr "о ϰ Ĩϴ" + +#, fuzzy +#~ msgid "Quiet operation" +#~ msgstr "۵ Ⱥ" + +#, fuzzy +#~ msgid "" +#~ "Error: No arguments found.\n" +#~ "Run '%s --help' to see a full list of available command line options.\n" +#~ msgstr "" +#~ "ɼ %s ֽϴ : %s. \n" +#~ "'%s --help' Ѽ ɼ ü Ͻʽÿ.\n" + +#, fuzzy +#~ msgid "" +#~ "%s error: must specify only one of -t or -o.\n" +#~ "Run '%s --help' to see a full list of available command line options.\n" +#~ msgstr "" +#~ "ɼ %s ֽϴ : %s. \n" +#~ "'%s --help' Ѽ ɼ ü Ͻʽÿ.\n" + +#, fuzzy +#~ msgid "%s error: no input file." +#~ msgstr "ùٸ Է ʿմϴ\n" + +#, fuzzy +#~ msgid "%s error: only one input file expected." +#~ msgstr "ùٸ Է ʿմϴ\n" + +#, fuzzy +#~ msgid "Background Colour" +#~ msgstr " :" + +#~ msgid "No object menu" +#~ msgstr "ü ޴ " + +#~ msgid "Create Text" +#~ msgstr "ڿ " + +#~ msgid "Create Ellipse" +#~ msgstr "Ÿ " + +#~ msgid "Create Polygon" +#~ msgstr "ٰ " + +#, fuzzy +#~ msgid "Create Beziergon" +#~ msgstr " " + +#~ msgid "Create Arc" +#~ msgstr "ȣ " + +#~ msgid "Create Zigzagline" +#~ msgstr " " + +#~ msgid "Create Polyline" +#~ msgstr "ἱ " + +#~ msgid "Create Bezierline" +#~ msgstr " " + +#~ msgid "Create Image" +#~ msgstr "׸ " + +#~ msgid "_New diagram" +#~ msgstr " ǥ(_N)" + +#, fuzzy +#~ msgid "_Diagram tree" +#~ msgstr "ǥ " + +#, fuzzy +#~ msgid "Show diagram tree" +#~ msgstr " ǥ(_N)" + +#, fuzzy +#~ msgid "_Sheets and Objects..." +#~ msgstr "ǥ ü" + +#, fuzzy +#~ msgid "P_lugins" +#~ msgstr "÷" + +#~ msgid "Page Set_up..." +#~ msgstr " (_u)..." + +#~ msgid "_Print Diagram..." +#~ msgstr "ǥ μ(_P)..." + +#~ msgid "Copy Text" +#~ msgstr "ڿ " + +#~ msgid "Cut Text" +#~ msgstr "ڿ ڸ" + +#~ msgid "Paste _Text" +#~ msgstr "ڿ (_T)" + +#~ msgid "Zoom _In" +#~ msgstr "Ȯ(_I)" + +#~ msgid "Zoom in 50%" +#~ msgstr "2 Ȯ" + +#~ msgid "Zoom _Out" +#~ msgstr "(_O)" + +#~ msgid "Zoom out 50%" +#~ msgstr "2 " + +#~ msgid "_Zoom" +#~ msgstr "Ȯ(_Z)" + +#~ msgid "_AntiAliased" +#~ msgstr "Ƽ̽(_A)" + +#, fuzzy +#~ msgid "Show _Grid" +#~ msgstr "ڸ :" + +#~ msgid "Show _Rulers" +#~ msgstr " (_R)" + +#~ msgid "Show _Connection Points" +#~ msgstr " (_C)" + +#~ msgid "Top" +#~ msgstr "" + +#~ msgid "Bottom" +#~ msgstr "Ʒ" + +#~ msgid "Send to _Back" +#~ msgstr "ڷ (_B)" + +#~ msgid "Bring to _Front" +#~ msgstr " (_F)" + +#, fuzzy +#~ msgid "Send Backwards" +#~ msgstr "ڷ (_B)" + +#~ msgid "_Group" +#~ msgstr "(_G)" + +#~ msgid "_Ungroup" +#~ msgstr "Ǯ(_U)" + +#~ msgid "Align _Horizontal" +#~ msgstr " (_H)" + +#~ msgid "Align _Vertical" +#~ msgstr " (_V)" + +#~ msgid "_Layers" +#~ msgstr "(_L)" + +#, fuzzy +#~ msgid "_Select" +#~ msgstr "" + +#, fuzzy +#~ msgid "_Objects" +#~ msgstr "/ü(_O)" + +#, fuzzy +#~ msgid "_Tools" +#~ msgstr "" + +#, fuzzy +#~ msgid "_Dialogs" +#~ msgstr "/ȭ(_D)" + +#, fuzzy +#~ msgid "/View/Diagram Properties..." +#~ msgstr "/ȭ/Ӽ(_P)" + +#~ msgid "/Objects/Align Horizontal/Center" +#~ msgstr "/ü/ /߰" + +#~ msgid "/Objects/Align Horizontal/Equal Distance" +#~ msgstr "/ü/ /" + +#~ msgid "/_Dialogs" +#~ msgstr "/ȭ(_D)" + +#~ msgid "/Dialogs/_Properties" +#~ msgstr "/ȭ/Ӽ(_P)" + +#~ msgid "Apply" +#~ msgstr "" + +#, fuzzy +#~ msgid "Error occured while printing" +#~ msgstr "PNG ߻߽ϴ" + +#, fuzzy +#~ msgid "Show at startup:" +#~ msgstr "Ҷ ڵ б" + +#, fuzzy +#~ msgid "Default width:" +#~ msgstr " :" + +#, fuzzy +#~ msgid "Can't open history file for writing." +#~ msgstr " ϴ: '%s' .\n" + +#~ msgid "Length: " +#~ msgstr ": " + +#~ msgid "Width: " +#~ msgstr ": " + +#, fuzzy +#~ msgid "Line gaps" +#~ msgstr " :" + +#, fuzzy +#~ msgid "Start at object edge" +#~ msgstr "ǥ ü" + +#, fuzzy +#~ msgid "Helvetica" +#~ msgstr "¿ ٲ" + +#~ msgid "Delete" +#~ msgstr "" + +#, fuzzy +#~ msgid "Could not load XSLT library (%s) : %s" +#~ msgstr "" +#~ "÷ `%s' ϴ\n" +#~ "%s" + +#, fuzzy +#~ msgid "A pnp bipolar transistor" +#~ msgstr "" + +#, fuzzy +#~ msgid "Create a flow" +#~ msgstr " " + +#, fuzzy +#~ msgid "Create a function" +#~ msgstr " " + +#, fuzzy +#~ msgid "A macro entry step" +#~ msgstr "ø" + +#, fuzzy +#~ msgid "A macro exit step" +#~ msgstr "ø" + +#, fuzzy +#~ msgid "A regular step" +#~ msgstr "ø" + +#, fuzzy +#~ msgid "A transition" +#~ msgstr "" + +#, fuzzy +#~ msgid "A Nand gate" +#~ msgstr "" + +#, fuzzy +#~ msgid "A Xor gate" +#~ msgstr "" + +#, fuzzy +#~ msgid "An And gate" +#~ msgstr "" + +#, fuzzy +#~ msgid "Create a branch" +#~ msgstr "ȣ " + +#, fuzzy +#~ msgid "Create a class" +#~ msgstr "ø Ŭ" + +#, fuzzy +#~ msgid "Create a component" +#~ msgstr "ٰ " + +#, fuzzy +#~ msgid "Create a fork/union" +#~ msgstr " " + +#, fuzzy +#~ msgid "Create a lifeline" +#~ msgstr " " + +#, fuzzy +#~ msgid "Create a message" +#~ msgstr "׸ " + +#, fuzzy +#~ msgid "Create a node" +#~ msgstr " " + +#, fuzzy +#~ msgid "Create a note" +#~ msgstr " " + +#, fuzzy +#~ msgid "Create a state" +#~ msgstr " " + +#, fuzzy +#~ msgid "Create a template class" +#~ msgstr "ø Ŭ" + +#, fuzzy +#~ msgid "Create a use case" +#~ msgstr "׸ " + +#, fuzzy +#~ msgid "Create an activity" +#~ msgstr " ǥ ϴ" + +#, fuzzy +#~ msgid "Create an actor" +#~ msgstr " ǥ ϴ" + +#, fuzzy +#~ msgid "Create an object" +#~ msgstr " " + +#~ msgid "/View/_Visible Grid" +#~ msgstr "// " + +#~ msgid "Corner rounding:" +#~ msgstr "ڸ ձ۰:" + +#~ msgid "Shear angle:" +#~ msgstr " :" + +#~ msgid "Begin" +#~ msgstr "" + +#~ msgid "End" +#~ msgstr "" + +#, fuzzy +#~ msgid "A Diagram Editor" +#~ msgstr "ǥ " + +#~ msgid "Image file:" +#~ msgstr "׸ :" + +#~ msgid "Keep aspect ratio:" +#~ msgstr " :" + +#~ msgid "Maintainer: James Henstridge" +#~ msgstr ": James Henstridge" + +#~ msgid "Fontsize:" +#~ msgstr "۲ ũ:" + +#~ msgid "Print Diagram" +#~ msgstr "ǥ μ" + +#, fuzzy +#~ msgid "An error occured while creating the print context" +#~ msgstr "PNG ߻߽ϴ" + +#~ msgid "yes" +#~ msgstr "" + +#~ msgid "no" +#~ msgstr "ƴϿ" + +#~ msgid "Load" +#~ msgstr "б" + +#~ msgid "Unload" +#~ msgstr "ϱ" + +#, fuzzy +#~ msgid "/File/Exit" +#~ msgstr "" + +#~ msgid "" +#~ "Warning no X Font for %s found, \n" +#~ "using %s instead.\n" +#~ msgstr "" +#~ "%s X ۲ ϴ. \n" +#~ "%s մϴ.\n" + +#, fuzzy +#~ msgid "Warning: No X fonts found. The world is ending." +#~ msgstr "" +#~ "%s X ۲ ϴ. \n" +#~ "%s մϴ.\n" + +#~ msgid "Quit, are you sure?" +#~ msgstr " Ͻðڽϱ?" + +#~ msgid "Quit" +#~ msgstr "" + +#~ msgid "Really close?" +#~ msgstr " ݰڽϱ?" + +#, fuzzy +#~ msgid "Down" +#~ msgstr "Ʒ ̵" + +#, fuzzy +#~ msgid "_Remove" +#~ msgstr "" + +#~ msgid "`%s' is not a directory" +#~ msgstr "`%s' ڷ ƴմϴ" + +#, fuzzy +#~ msgid "Number of processes:" +#~ msgstr "ϱ :" + +#, fuzzy +#~ msgid "multiple" +#~ msgstr "ߺ" + +#, fuzzy +#~ msgid "Single" +#~ msgstr "" + +#, fuzzy +#~ msgid "Multiple" +#~ msgstr "ߺ" + +#, fuzzy +#~ msgid "Instantiation" +#~ msgstr "" + +#, fuzzy +#~ msgid "Unidirectional" +#~ msgstr "ռ" + +#, fuzzy +#~ msgid "Interaction name:" +#~ msgstr " ̽" + +#, fuzzy +#~ msgid "relation" +#~ msgstr "۵" + +#, fuzzy +#~ msgid "Interface functions" +#~ msgstr " " + +#, fuzzy +#~ msgid "Interfaces" +#~ msgstr " ̽" + +#, fuzzy +#~ msgid "Interface" +#~ msgstr " ̽" + +#, fuzzy +#~ msgid "Interface name:" +#~ msgstr " ̽" + +#, fuzzy +#~ msgid "Interface messages" +#~ msgstr "" + +#, fuzzy +#~ msgid "Message parameters" +#~ msgstr ":" + +#, fuzzy +#~ msgid "Functions" +#~ msgstr ":" + +#, fuzzy +#~ msgid "Messages" +#~ msgstr "" + +#, fuzzy +#~ msgid "Process name:" +#~ msgstr "Ŭ̸:" + +#, fuzzy +#~ msgid "Process reference name:" +#~ msgstr "" + +#, fuzzy +#~ msgid "Module name:" +#~ msgstr " ̸:" + +#, fuzzy +#~ msgid "Parameter" +#~ msgstr ":" + +#~ msgid "Grid:" +#~ msgstr ":" + +#~ msgid "" +#~ "No such file found\n" +#~ "%s\n" +#~ msgstr "" +#~ " ϴ\n" +#~ "%s\n" + +#~ msgid "Import from XFig" +#~ msgstr "XFig ȯ" + +#, fuzzy +#~ msgid "File/New diagram" +#~ msgstr " ǥ(_N)" + +#, fuzzy +#~ msgid "File/Save As..." +#~ msgstr "// (_u)..." + +#, fuzzy +#~ msgid "File/Close" +#~ msgstr "//ݱ(_C)" + +#, fuzzy +#~ msgid "View/New View" +#~ msgstr "// (_V)" + +#~ msgid "/Edit/Copy" +#~ msgstr "//" + +#~ msgid "/Edit/Paste" +#~ msgstr "//̱" + +#~ msgid "/Edit/Delete" +#~ msgstr "//" + +#~ msgid "/Edit/Paste Text" +#~ msgstr "//ڿ ̱" + +#~ msgid "/Objects/Group" +#~ msgstr "/ü/" + +#~ msgid "/Objects/Ungroup" +#~ msgstr "/ü/ǰ" + +#~ msgid "/View/Show Rulers" +#~ msgstr "// " + +#~ msgid "/View/Visible Grid" +#~ msgstr "// " + +#~ msgid "/View/Snap To Grid" +#~ msgstr "// " + +#~ msgid "/View/Show Connection Points" +#~ msgstr "// " + +#~ msgid "/View/AntiAliased" +#~ msgstr "//Ƽ̽" + +#, fuzzy +#~ msgid "/Edit/tearoff" +#~ msgstr "//̱" + +#, fuzzy +#~ msgid "/View/tearoff" +#~ msgstr "//1" + +#, fuzzy +#~ msgid "/View/Zoom/tearoff" +#~ msgstr "//Ȯ(_Z)" + +#~ msgid "/View/sep1" +#~ msgstr "//1" + +#, fuzzy +#~ msgid "/Select/tearoff" +#~ msgstr "//ݴ" + +#~ msgid "/Select/sep1" +#~ msgstr "//1" + +#, fuzzy +#~ msgid "/Objects/tearoff1" +#~ msgstr "/ü/1" + +#~ msgid "/Objects/sep1" +#~ msgstr "/ü/1" + +#, fuzzy +#~ msgid "/Objects/Align Horizontal/tearoff" +#~ msgstr "/ü/ /߰" + +#, fuzzy +#~ msgid "/Objects/Align Vertical/tearoff" +#~ msgstr "/ü/ /߰" + +#, fuzzy +#~ msgid "/Tools/tearoff" +#~ msgstr "//" + +#, fuzzy +#~ msgid "/Dialogs/tearoff" +#~ msgstr "/ȭ/(_L)" + +#~ msgid "Asynchronous" +#~ msgstr "񵿱" + +#~ msgid "Grid y size:" +#~ msgstr " ũ:" + +#~ msgid "Edit Grid..." +#~ msgstr " ..." + +#~ msgid "/View/Edit Grid..." +#~ msgstr "// ..." diff --git a/vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.mo b/vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.mo new file mode 100644 index 0000000000000000000000000000000000000000..50b03cbf237ce4c6119ec42f6a8cde460c8bc071 GIT binary patch literal 1009843 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 z⪚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

TL36ByCCvU`G6kr)-mCQIX3$80l67*(KEF-jP` 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 z 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.po b/vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.po new file mode 100644 index 0000000000..d38622bd2f --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/mm-viet.comp.po @@ -0,0 +1,49553 @@ +# Vietnamese translation for NAME. +# Copyright © 2006 Gnome i18n Project for Vietnamese. +# Clytie Siddall , 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: NAME_VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-02-03 02:05+0100\n" +"PO-Revision-Date: #LOCALTIME %F %R%z#\n" +"Last-Translator: Clytie Siddall \n" +"Language-Team: Vietnamese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"X-Generator: LocFactoryEditor 1.6b36\n" + +# Name: don't translate / Tên: đừng dịch +#: ../balsa.desktop.in.h:1 +#: ../capplets/default-applications/gnome-default-applications-properties-structs.c:42 +msgid "Balsa" +msgstr "Balsa" + +#: ../balsa.desktop.in.h:2 +msgid "E-Mail utility" +msgstr "Tiện ích thư điện tử" + +#: ../libbalsa/address-book-ldif.c:329 +msgid "No-Id" +msgstr "Không có ID" + +#: ../libbalsa/address-book-vcard.c:312 +msgid "No-Name" +msgstr "Không có tên" + +#: ../libbalsa/address-book-ldap.c:275 +msgid "TLS requested but not compiled in" +msgstr "TLS được yêu cầu còn không biên dịch sẵn." + +#: ../libgnomevfs/gnome-vfs-result.c:42 +msgid "No error" +msgstr "Không có lỗi" + +#: ../libbalsa/address-book.c:302 +msgid "Cannot read from address book" +msgstr "Không thể đọc từ sổ địa chỉ" + +#: ../libbalsa/address-book.c:303 +msgid "Cannot write to address book" +msgstr "Không thể ghi vào sổ địa chỉ" + +#: ../libbalsa/address-book.c:304 ../libbalsa/imap-server.c:511 +msgid "Cannot connect to the server" +msgstr "Không thể kết nối đến máy phục vụ" + +#: ../libbalsa/address-book.c:305 +msgid "Cannot search in the address book" +msgstr "Không thể tìm kiếm trong sổ địa chỉ" + +#: ../libbalsa/address-book.c:307 +msgid "Cannot add duplicate entry" +msgstr "Không thể thêm mục nhập trùng" + +#: ../libbalsa/address-book.c:309 +msgid "Cannot find address in address book" +msgstr "Không tìm thấy địa chỉ trong sổ địa chỉ" + +#: ../testing/gda-test-sql.c:171 ../testing/gda-test-sql.c:392 +msgid "Unknown error" +msgstr "Gặp lỗi lạ" + +#: ../libbalsa/address.c:436 +msgid "_Displayed Name:" +msgstr "Tên _hiển thị :" + +#: ../libbalsa/address.c:437 ../ui/addcontact.glade.h:6 +#: ../ui/user_info.glade.h:77 +msgid "_First Name:" +msgstr "T_ên:" + +#: ../libbalsa/address.c:438 ../ui/addcontact.glade.h:8 +#: ../ui/user_info.glade.h:81 +msgid "_Last Name:" +msgstr "_Họ :" + +#: ../libbalsa/address.c:439 ../libgames-support/games-network-dialog.c:266 +#: ../ui/addcontact.glade.h:9 ../ui/user_info.glade.h:82 +msgid "_Nickname:" +msgstr "Tên _hiệu :" + +#: ../libbalsa/address.c:440 +msgid "O_rganization:" +msgstr "_Tổ chức:" + +#: ../libbalsa/address.c:441 ../pan/identities/identity-edit-ui.c:142 +msgid "_Email Address:" +msgstr "_Địa chỉ thư điện tử :" + +#: ../libbalsa/body.c:115 ../libbalsa/mailbox_local.c:1644 +#: ../libbalsa/message.c:855 +msgid "(No subject)" +msgstr "(Không có chủ đề)" + +#: ../libbalsa/files.c:231 ../src/nautilus-information-panel.c:898 +#, c-format +msgid "Open with %s" +msgstr "Mở bằng %s" + +#: ../libbalsa/filter-error.c:46 +msgid "Syntax error in the filter configuration file" +msgstr "Gặp lỗi cú pháp trong tập tin cấu hình bộ lọc." + +#: ../libbalsa/filter-error.c:47 +msgid "Unable to allocate memory" +msgstr "Không thể cấp phát bộ nhớ." + +#: ../libbalsa/filter-error.c:48 +msgid "Error in regular expression syntax" +msgstr "Gặp lỗi trong cú pháp biểu thức chính quy." + +#: ../libbalsa/filter-error.c:49 +msgid "Attempt to apply an invalid filter" +msgstr "Việc cố áp dụng một bộ lọc không hợp lệ" + +#: ../libbalsa/filter-file.c:141 +#, c-format +msgid "Invalid filters %s for mailbox %s" +msgstr "Bộ lọc %s không hợp lệ cho hộp thư %s" + +#: ../libbalsa/filter.c:132 ../libbalsa/mailbox_local.c:769 +msgid "Unable to load message body to match filter" +msgstr "Không thể tải thân thư để khớp bộ lọc." + +#: ../libbalsa/filter.c:237 +#, c-format +msgid "Invalid filter: %s" +msgstr "Bộ lọc không hợp lệ: %s" + +#: ../libbalsa/filter.c:276 ../libbalsa/filter.c:299 +#, c-format +msgid "Bad mailbox name for filter: %s" +msgstr "Tên hộp thư sai cho bộ lọc: %s" + +#: ../libbalsa/filter.c:280 +#, c-format +msgid "Error when copying messages: %s" +msgstr "Gặp lỗi khi sao chép thư : %s" + +#: ../libbalsa/filter.c:290 +#, c-format +msgid "Error when trashing messages: %s" +msgstr "Gặp lỗi khi xoá bỏ thư : %s" + +#: ../libbalsa/filter.c:303 +#, c-format +msgid "Error when moving messages: %s" +msgstr "Gặp lỗi khi di chuyển thư : %s" + +#: ../libbalsa/gmime-application-pkcs7.c:237 +#: ../libbalsa/gmime-application-pkcs7.c:380 +msgid "Failed to decrypt MIME part: parse error" +msgstr "Việc giải mật mã phần MIME bị lỗi: lỗi phân tách" + +#: ../libbalsa/gmime-gpgme-context.c:279 ../libbalsa/rfc3156.c:1596 +#, c-format +msgid "" +"The passphrase for this key was bad, please try again!\n" +"\n" +"Key: %s" +msgstr "" +"Bạn đã gõ sai cụm từ mật khẩu cho khoá này. Hãy thử lại.\n" +"\n" +"Khoá: %s" + +#: ../libbalsa/gmime-gpgme-context.c:284 ../libbalsa/rfc3156.c:1601 +#, c-format +msgid "" +"Please enter the passphrase for the secret key!\n" +"\n" +"Key: %s" +msgstr "" +"• Hãy gõ cụm từ mật khẩu cho khoá bí mật. •\n" +"\n" +"Khoá: %s" + +#: ../libbalsa/gmime-gpgme-context.c:404 ../libbalsa/gmime-gpgme-context.c:486 +#: ../libbalsa/gmime-gpgme-context.c:497 ../libbalsa/gmime-gpgme-context.c:612 +#: ../libbalsa/gmime-gpgme-context.c:703 +#, c-format +msgid "%s: could not get data from stream: %s" +msgstr "%s: không thể lấy dữ liệu từ luồng: %s" + +#: ../libbalsa/gmime-gpgme-context.c:412 ../libbalsa/gmime-gpgme-context.c:621 +#: ../libbalsa/gmime-gpgme-context.c:711 +#, c-format +msgid "%s: could not create new data object: %s" +msgstr "%s: không thể tạo đối tượng dữ liệu mới: %s" + +#: ../libbalsa/gmime-gpgme-context.c:422 +#, c-format +msgid "%s: signing failed: %s" +msgstr "%s: việc ký tên bị lỗi: %s" + +#: ../libbalsa/gmime-gpgme-context.c:511 +#, c-format +msgid "%s: signature verification failed: %s" +msgstr "%s: việc thẩm tra chữ ký bị lỗi: %s" + +#: ../libbalsa/gmime-gpgme-context.c:576 +msgid "combined signing and encryption is only defined for RFC 2440" +msgstr "" +"khả năng ký tên và mật mã được tổ hợp với nhau chỉ được định nghĩa cho RFC " +"2440" + +#: ../libbalsa/gmime-gpgme-context.c:647 +#, c-format +msgid "%s: signing and encryption failed: %s" +msgstr "%s: việc ký tên và mật mã bị lỗi: %s" + +#: ../libbalsa/gmime-gpgme-context.c:651 +#, c-format +msgid "%s: encryption failed: %s" +msgstr "%s: việc mật mã bị lỗi: %s" + +#: ../libbalsa/gmime-gpgme-context.c:723 +#, c-format +msgid "%s: decryption failed: %s" +msgstr "%s: việc giải mật mã bị lỗi: %s" + +#: ../libbalsa/gmime-gpgme-context.c:758 +#, c-format +msgid "%s: could not create context: %s" +msgstr "%s: không thể tạo ngữ cảnh: %s" + +#: ../libbalsa/gmime-gpgme-context.c:814 +msgid "the crypto engine for protocol OpenPGP is not available" +msgstr "cơ chế mật mã cho giao thức OpenPGP chưa sẵn sàng" + +#: ../libbalsa/gmime-gpgme-context.c:824 +msgid "the crypto engine for protocol CMS is not available" +msgstr "Vcơ chế mật mã cho giao thức CMS chưa sẵn sàng" + +#: ../libbalsa/gmime-gpgme-context.c:831 +#, c-format +msgid "invalid crypto engine %d" +msgstr "Động cơ mật mã không hợp lệ %d" + +#: ../libbalsa/gmime-gpgme-context.c:862 ../libbalsa/gmime-gpgme-context.c:884 +#, c-format +msgid "%s: could not list keys for %s: %s" +msgstr "%s: không thể liệt kê khoá cho %s: %s" + +#: ../libbalsa/gmime-gpgme-context.c:896 +#, c-format +msgid "%s: could not find a key for %s" +msgstr "%s: không thể tìm khoá cho %s" + +#: ../libbalsa/gmime-gpgme-context.c:908 +#, c-format +msgid "%s: multiple keys for %s" +msgstr "%s: có nhiều khoá cho %s" + +#: ../libbalsa/gmime-gpgme-context.c:953 +#, c-format +msgid "%s: insufficient validity for uid %s" +msgstr "%s: không có đủ độ hợp lệ cho mã nhận diện người dùng (UID) %s" + +#: ../src/sendmsg-window.c:3501 +msgid "Re:" +msgstr "Về:" + +#: ../libbalsa/identity.c:103 ../src/sendmsg-window.c:3531 +#: ../src/sendmsg-window.c:3532 +msgid "Fwd:" +msgstr "Tiếp:" + +#: ../libbalsa/identity.c:160 +msgid "New Identity" +msgstr "Thực thể mới" + +#: ../libbalsa/identity.c:413 ../objects/FS/function.c:1124 +#: datebook_gui.c:1582 app/envelope-box.c:879 +msgid "Current" +msgstr "Hiện có" + +#: ../src/session.c:2276 ../users-conf.in:496 ../widgets/gtk+.xml.in.h:48 +msgid "Default" +msgstr "Mặc định" + +#: ../libbalsa/identity.c:862 +msgid "Signature _Path" +msgstr "_Đường dẫn chữ ký" + +#. create the "Signature" tab +#: ../libbalsa/identity.c:866 ../libbalsa/identity.c:920 +msgid "Signature" +msgstr "Chữ ký" + +#. Translators: please do not translate Face. +#: ../libbalsa/identity.c:868 +msgid "_Face Path" +msgstr "Đường dẫn _mặt" + +#. Translators: please do not translate Face. +#: ../libbalsa/identity.c:874 +msgid "_X-Face Path" +msgstr "Đường dẫn mặt _X" + +#: ../glade/glade_project_options.c:174 ../ui/user_info.glade.h:26 +#: ../src/glade-editor.c:815 ../src/dialog-win.cc:86 +#: ../src/form-editor/list-prop.cc:83 ../src/form-editor/table-prop.cc:153 +msgid "General" +msgstr "Chung" + +#: ../libbalsa/identity.c:898 +msgid "_Identity Name:" +msgstr "T_ên thực thể" + +#: ../libbalsa/identity.c:900 ../mail/mail-config.glade.h:160 +#: ../pan/identities/identity-edit-ui.c:139 +msgid "_Full Name:" +msgstr "_Họ tên:" + +#: ../libbalsa/identity.c:902 +msgid "_Mailing Address:" +msgstr "Đị_a chỉ thư tín:" + +#: ../libbalsa/identity.c:904 +msgid "Reply _To:" +msgstr "T_rả lời cho :" + +#: ../libbalsa/identity.c:906 ../libgnomeui/gnome-password-dialog.c:254 +msgid "_Domain:" +msgstr "_Miền:" + +#: ../libbalsa/identity.c:908 ../composer/e-msg-composer-hdrs.c:654 +#: ../composer/e-msg-composer-hdrs.c:656 +msgid "_Bcc:" +msgstr "_Bcc:" + +#: ../libbalsa/identity.c:910 +msgid "Reply _String:" +msgstr "_Chuỗi trả lời:" + +#: ../libbalsa/identity.c:912 +msgid "F_orward String:" +msgstr "Chuỗi chuyển _tiếp:" + +#: ../libbalsa/identity.c:915 +msgid "SMT_P Server:" +msgstr "Máy phục vụ SMT_P:" + +#: ../libbalsa/identity.c:925 +msgid "_Execute Signature" +msgstr "T_hực hiện chữ ký" + +#: ../libbalsa/identity.c:928 +msgid "Incl_ude Signature" +msgstr "_Kèm theo chữ ký" + +#: ../libbalsa/identity.c:931 +msgid "Include Signature When For_warding" +msgstr "Kèm theo chữ ký khi gởi chu_yển tiếp" + +#: ../libbalsa/identity.c:934 +msgid "Include Signature When Rep_lying" +msgstr "Kèm theo chữ ký khi t_rả lời" + +#: ../libbalsa/identity.c:937 +msgid "_Add Signature Separator" +msgstr "Th_êm bộ phân cách chữ ký" + +#: ../libbalsa/identity.c:940 +msgid "Prepend Si_gnature" +msgstr "Kèm tr_ước chữ ký" + +#: ../gnomecard/card-editor.glade.h:37 +msgid "Security" +msgstr "Bảo mật" + +#: ../libbalsa/identity.c:955 +msgid "sign messages by default" +msgstr "ký tên thư theo mặc định" + +#: ../libbalsa/identity.c:958 +msgid "encrypt messages by default" +msgstr "mật mã thư theo mặc định" + +#: ../libbalsa/identity.c:961 +msgid "default protocol" +msgstr "giao thức mặc định" + +#: ../libbalsa/identity.c:964 +msgid "always trust GnuPG keys when encrypting" +msgstr "luôn tin khoá GnuGP khi mật mã hóa" + +#: ../libbalsa/identity.c:1069 +#, c-format +msgid "Error reading file %s: %s" +msgstr "Gặp lỗi khi đọc tập tin %s: %s" + +#. Translators: please do not translate Face. +#: ../libbalsa/identity.c:1080 +#, c-format +msgid "Face header file %s is too long (%d bytes)." +msgstr "Tập tin phần đầu mặt %s quá dài (%d byte)." + +#. Translators: please do not translate Face. +#: ../libbalsa/identity.c:1090 +#, c-format +msgid "Face header file %s contains binary data." +msgstr "Tập tin phần đầu mặt %s chứa dữ liệu nhị phân." + +#. Translators: please do not translate Face. +#: ../libbalsa/identity.c:1110 ../src/balsa-message.c:1245 +#, c-format +msgid "Error loading Face: %s" +msgstr "Gặp lỗi khi tải Face: %s" + +#: ../libbalsa/identity.c:1299 +msgid "Error: The identity does not have a name" +msgstr "Lỗi: thực thể không có tên" + +#: ../libbalsa/identity.c:1309 +msgid "Error: An identity with that name already exists" +msgstr "Lỗi: một thực thể cùng tên đã có." + +#: ../libbalsa/identity.c:1501 +msgid "Do you really want to delete the selected identity?" +msgstr "Bạn thật sự muốn xoá bỏ thực thể đã chọn không?" + +#: ../libbalsa/identity.c:1536 +#, c-format +msgid "Error displaying help for identities: %s\n" +msgstr "Gặp lỗi khi hiển thị trợ giúp cho thực thể: %s\n" + +#: ../libbalsa/identity.c:1582 +msgid "Manage Identities" +msgstr "Quản lý thực thể" + +#: ../libbalsa/identity.c:1939 +msgid "GnuPG MIME mode" +msgstr "Chế độ MIME GnuPG" + +#: ../libbalsa/identity.c:1941 +msgid "GnuPG OpenPGP mode" +msgstr "Chế độ OpenPGP GnuPG" + +#: ../libbalsa/identity.c:1944 +msgid "GpgSM S/MIME mode" +msgstr "Chế độ S/MIME GnuPG" + +#. IMAP host name + message +#: ../libbalsa/imap-server.c:256 +#, c-format +msgid "" +"IMAP server %s alert:\n" +"%s" +msgstr "" +"Cảnh giác máy phục vụ IMAP %s:\n" +"%s" + +#. IMAP host name + message +#: ../libbalsa/imap-server.c:260 +#, c-format +msgid "IMAP server %s error: %s" +msgstr "Lỗi máy phục vụ IMAP %s: %s" + +#: ../libgnomecups/gnome-cups-printer.c:1043 misc.c:326 +#: app/drivers/sun-input.c:229 app/drivers/sun-input.c:311 +#: app/drivers/sun-output.c:389 app/drivers/sun-output.c:460 +#, c-format +msgid "%s: %s" +msgstr "%s: %s" + +#: ../libbalsa/imap-server.c:516 ../libbalsa/imap-server.c:521 +#, c-format +msgid "Cannot connect to the server: %s" +msgstr "Không thể kết nối đến máy phục vụ : %s" + +#: ../libbalsa/imap-server.c:669 +#, c-format +msgid "Exceeded the number of connections per server %s" +msgstr "Vượt quá số kết nối cho mỗi máy phục vụ %s" + +#: ../libbalsa/libbalsa-conf.c:203 +msgid "Your Balsa configuration is now stored in \"~/.balsa/config\"." +msgstr "Cấu hình Balsa của bạn bây giờ được cất giữ vào <~/.balsa/config>." + +#: ../libbalsa/libbalsa.c:285 +#, c-format +msgid "LDAP Directory for %s" +msgstr "Thư mục LDAP cho %s" + +#: ../libbalsa/libbalsa.c:391 +msgid "Invalid date" +msgstr "Ngày không hợp lệ" + +#: ../gmedia_prop/setup_alias_stuff.c:128 libexif/exif-entry.c:426 +#: libexif/exif-entry.c:441 libexif/exif-entry.c:522 libexif/exif-tag.c:672 +#: libexif/olympus/mnote-olympus-entry.c:452 +#: libexif/olympus/mnote-olympus-entry.c:508 ../src/orca/rolenames.py:483 +msgid "Unknown" +msgstr "Không rõ" + +#: ../libbalsa/libbalsa.c:546 +#, c-format +msgid "" +"Authenticity of this certificate could not be verified.\n" +"Reason: %s\n" +"This certificate belongs to:\n" +msgstr "" +"Không thể thẩm tra sự xác thực của chứng nhận này.\n" +"Lý do : %s\n" +"Chứng nhận này thuộc về:\n" + +#: ../libbalsa/libbalsa.c:558 +msgid "" +"\n" +"This certificate was issued by:\n" +msgstr "" +"\n" +"Chứng nhận này được phát hành bởi:\n" + +#: ../libbalsa/libbalsa.c:569 +#, c-format +msgid "" +"This certificate is valid\n" +"from %s\n" +"to %s\n" +"Fingerprint: %s" +msgstr "" +"Chứng nhận này hợp lệ\n" +"từ %s\n" +"đến %s\n" +"Dấu điểm chỉ: %s" + +#: ../libbalsa/libbalsa.c:578 +msgid "SSL/TLS certificate" +msgstr "Chứng nhận SSL/TLS" + +#: ../libbalsa/libbalsa.c:580 +msgid "_Accept Once" +msgstr "_Chấp nhận một lần" + +#: ../libbalsa/libbalsa.c:581 +msgid "Accept&_Save" +msgstr "Chấp nhận và _Lưu" + +#: ../libbalsa/libbalsa.c:582 mozilla/CookiePromptService.cpp:151 +msgid "_Reject" +msgstr "_Từ chối" + +#: ../libbalsa/mailbox.c:400 +#, c-format +msgid "Cannot load mailbox %s" +msgstr "Không thể tải hộp thư %s" + +#: ../libbalsa/mailbox.c:407 +#, c-format +msgid "No such mailbox type: %s" +msgstr "Không có kiểu hộp thư như vậy: %s" + +#: ../libbalsa/mailbox.c:424 +#, c-format +msgid "Bad local mailbox path \"%s\"" +msgstr "Đường dẫn hộp thư cục bộ sai « %s »" + +#: ../libbalsa/mailbox.c:429 +#, c-format +msgid "Could not create a mailbox of type %s" +msgstr "Không thể tạo hộp thư kiểu %s" + +#: ../libbalsa/mailbox.c:736 +#, c-format +msgid "Applying filter rules to %s" +msgstr "Đang áp dụng các quy tắc lọc cho %s..." + +#: ../libbalsa/mailbox.c:835 +#, c-format +msgid "Copying from %s to %s" +msgstr "Đang sao chép %s sang %s..." + +#: ../libbalsa/mailbox.c:1779 +msgid "Removing messages from source mailbox failed" +msgstr "Việc gỡ bỏ thư ra hộp thư nguồn bị lỗi." + +#: ../libbalsa/mailbox.c:3559 +#, c-format +msgid "Searching %s for partial messages" +msgstr "Đang tìm kiếm trong %s có thư riêng phần..." + +#: ../libbalsa/mailbox.c:3649 +msgid "Reconstructing message" +msgstr "Đang cấu tạo lại thư..." + +#. ImapIssue macro handles reconnecting. We might issue a +#. LIBBALSA_INFORMATION_MESSAGE here but it would be overwritten by +#. login information... +#: ../libbalsa/mailbox_imap.c:539 +msgid "IMAP connection has been severed. Reconnecting..." +msgstr "Kết nối IMAP đã bị ngắt. Đang tái kết nối..." + +#: ../libbalsa/mailbox_imap.c:542 +#, c-format +msgid "IMAP server has shut the connection: %s Reconnecting..." +msgstr "Máy phục vụ IMAP đã đóng kết nối: %s. Đang tái kết nối..." + +#: ../libbalsa/mailbox_imap.c:1014 ../libbalsa/mailbox_maildir.c:527 +#: ../libbalsa/mailbox_mbox.c:716 ../libbalsa/mailbox_mh.c:553 +#, c-format +msgid "%s: Opening %s Refcount: %d\n" +msgstr "%s: Đang mở %s Số đếm tham chiếu : %d\n" + +#: ../libbalsa/mailbox_imap.c:1088 ../libbalsa/mailbox_imap.c:2228 +#, c-format +msgid "Downloading %ld kB" +msgstr "Đang tải về %ld kB..." + +#: ../libbalsa/mailbox_imap.c:1468 +#, c-format +msgid "" +"IMAP SEARCH command failed for mailbox %s\n" +"falling back to default searching method" +msgstr "" +"Lệnh tìm kiếm IMAP bị lỗi cho hộp thư %s\n" +"như thế thì đang dùng phương pháp tìm kiếm mặc định." + +#: ../libbalsa/mailbox_imap.c:1528 +#, c-format +msgid "No path found for mailbox \"%s\", using \"%s\"" +msgstr "Không tìm thấy đường dẫn cho hộp thư « %s », dùng « %s »." + +#: ../libbalsa/mailbox_imap.c:1617 ../libbalsa/mailbox_imap.c:1658 +msgid "Cannot get IMAP handle" +msgstr "Không thể lấy bộ quản lý IMAP" + +#: ../libbalsa/mailbox_imap.c:2261 +msgid "Cannot create temporary file" +msgstr "Không thể tạo tập tin tạm thời." + +#: ../libbalsa/mailbox_imap.c:2280 +#, c-format +msgid "Cannot write to temporary file %s" +msgstr "Không thể ghi vào tập tin tạm thời %s." + +#: ../libbalsa/mailbox_imap.c:2497 +#, c-format +msgid "Uploading %ld kB" +msgstr "Đang tải lên %ld kB" + +#: ../libbalsa/mailbox_imap.c:2681 +msgid "Server-side threading not supported." +msgstr "Khả năng sắp xếp theo nhánh bên máy phục vụ không được hỗ trợ." + +#: ../libbalsa/mailbox_local.c:493 ../libbalsa/mailbox_mbox.c:412 +#, c-format +msgid "Failed to create temporary file \"%s\": %s" +msgstr "Việc tạo tập tin tạm thời « %s » bị lỗi: %s." + +#: ../libbalsa/mailbox_local.c:512 +#, c-format +msgid "Failed to save cache file \"%s\": %s." +msgstr "Việc lưu tập tin lưu tạm « %s » bị lỗi: %s." + +#: ../libbalsa/mailbox_local.c:522 ../libbalsa/mailbox_mbox.c:423 +#, c-format +msgid "Failed to save cache file \"%s\": %s. New version saved as \"%s\"" +msgstr "" +"Việc lưu tập tin lưu tạm « %s » bị lỗi: %s. Phiên bản mới đã được lưu dạng « " +"%s »." + +#: ../libbalsa/mailbox_local.c:553 +#, c-format +msgid "Cache file for mailbox %s will be created" +msgstr "Sẽ tạo tập tin lưu tạm cho hộp thư %s." + +#: ../libbalsa/mailbox_local.c:562 +#, c-format +msgid "Failed to read cache file %s: %s" +msgstr "Việc đọc tập tin lưu tạm %s bị lỗi: %s." + +#: ../libbalsa/mailbox_local.c:580 ../libbalsa/mailbox_local.c:595 +#: ../libbalsa/mailbox_local.c:618 +#, c-format +msgid "Cache file for mailbox %s will be repaired" +msgstr "Sẽ sửa chữa tập tin lưu tạm cho hộp thư %s." + +#: ../libbalsa/mailbox_local.c:1054 +#, c-format +msgid "Filtering %s" +msgstr "Đang lọc %s..." + +#: ../libbalsa/mailbox_local.c:1142 apt-pkg/deb/dpkgpm.cc:358 +#, c-format +msgid "Preparing %s" +msgstr "Đang chuẩn bị %s..." + +#: ../libbalsa/mailbox_local.c:1924 +#, c-format +msgid "Open of %s failed. Errno = %d, " +msgstr "Việc mở %s bị lỗi. Lỗi số = %d, " + +#: ../libbalsa/mailbox_local.c:1946 +#, c-format +msgid "Failed to sync mailbox \"%s\"" +msgstr "Việc đồng bộ hóa hộp thư « %s » bị lỗi." + +#: ../libbalsa/mailbox_maildir.c:216 +#, c-format +msgid "Mailbox %s does not appear to be a Maildir mailbox." +msgstr "Hình như hộp thư %s không phải là hộp thư kiểu Maildir." + +#: ../libbalsa/mailbox_maildir.c:225 +#, c-format +msgid "Could not create a MailDir directory at %s (%s)" +msgstr "Không thể tạo một thư mục MailDir tại %s (%s)." + +#: ../libbalsa/mailbox_maildir.c:233 ../libbalsa/mailbox_maildir.c:242 +#: ../libbalsa/mailbox_maildir.c:253 +#, c-format +msgid "Could not create a MailDir at %s (%s)" +msgstr "Không thể tạo một thư mục MailDir tại %s (%s)." + +#: ../libbalsa/mailbox_maildir.c:360 ../libbalsa/mailbox_mh.c:311 +#, c-format +msgid "" +"Could not remove contents of %s:\n" +"%s" +msgstr "" +"Không thể gỡ bỏ nội dung của %s:\n" +"%s" + +#: ../libbalsa/mailbox_maildir.c:366 ../libbalsa/mailbox_mbox.c:309 +#: ../libbalsa/mailbox_mh.c:317 +#, c-format +msgid "" +"Could not remove %s:\n" +"%s" +msgstr "" +"Không thể gỡ bỏ %s:\n" +"%s" + +#: ../libbalsa/mailbox_maildir.c:506 ../libbalsa/mailbox_mbox.c:663 +#: ../libbalsa/mailbox_mh.c:534 +msgid "Mailbox does not exist." +msgstr "Hộp thư không tồn tại." + +#: ../libbalsa/mailbox_maildir.c:975 ../libbalsa/mailbox_mbox.c:1976 +#: ../libbalsa/mailbox_mh.c:1160 +msgid "Data copy error" +msgstr "Lỗi sao chép dữ liệu" + +#: ../libbalsa/mailbox_mbox.c:211 +#, c-format +msgid "Mailbox %s does not appear to be an Mbox mailbox." +msgstr "Hộp thư %s có vẻ không phải là một hộp thư kiểu Mbox." + +#: ../libbalsa/mailbox_mbox.c:397 +#, c-format +msgid "Could not write file %s: %s" +msgstr "Không thể ghi tập tin %s: %s" + +#: ../libbalsa/mailbox_mbox.c:430 +#, c-format +msgid "Could not unlink file %s: %s" +msgstr "Không thể bỏ liên kết tập tin %s: %s" + +#: ../libbalsa/mailbox_mbox.c:671 +msgid "Cannot open mailbox." +msgstr "Không thể mở hộp thư." + +#: ../libbalsa/mailbox_mbox.c:683 +msgid "Mailbox is not in mbox format." +msgstr "Hộp thư không phải dạng mbox." + +#: ../libbalsa/mailbox_mbox.c:691 +msgid "Cannot lock mailbox." +msgstr "Không thể khoá hộp thư." + +#: ../libbalsa/mailbox_mbox.c:1933 +#, c-format +msgid "%s: could not open %s." +msgstr "%s: không thể mở %s." + +#: ../libbalsa/mailbox_mbox.c:1944 +#, c-format +msgid "%s: could not get new mime stream." +msgstr "%s: không thể lấy luông MIME mới." + +#: ../libbalsa/mailbox_mbox.c:1952 +#, c-format +msgid "%s: %s is not in mbox format." +msgstr "%s: %s không phải dạng mbox." + +#: ../libbalsa/mailbox_mh.c:192 +#, c-format +msgid "Mailbox %s does not appear to be a Mh mailbox." +msgstr "Hộp thư %s có vẻ không phải là hộp thư dạng Mh." + +#: ../libbalsa/mailbox_mh.c:202 +#, c-format +msgid "Could not create MH directory at %s (%s)" +msgstr "Không thể tạo thư mục MH tại %s (%s)." + +#: ../libbalsa/mailbox_mh.c:214 +#, c-format +msgid "Could not create MH structure at %s (%s)" +msgstr "Không thể tạo cấu trúc MH tại %s (%s)." + +#: ../libbalsa/mailbox_mh.c:1141 +msgid "Cannot create message" +msgstr "Không thể tạo thư." + +#: ../libbalsa/mailbox_mh.c:1185 +msgid "Message rename error" +msgstr "Lỗi thay đổi tên thư." + +#: ../libbalsa/mailbox_pop3.c:189 +#, c-format +msgid "Error appending message %d from %s to %s: %s" +msgstr "Gặp lỗi khi phụ thêm thư %d từ %s vào %s: %s" + +#: ../libbalsa/mailbox_pop3.c:338 +#, c-format +msgid "Saving POP message to %s failed" +msgstr "Việc lưu thư POP vào %s bị lỗi." + +#: ../libbalsa/mailbox_pop3.c:345 +#, c-format +msgid "Retrieving Message %d of %d" +msgstr "Đang lấy thư %d trên %d..." + +#: ../libbalsa/mailbox_pop3.c:368 +#, c-format +msgid "Received %ld kB of %ld" +msgstr "Đã nhận %ld kB trên %ld." + +#: ../libbalsa/mailbox_pop3.c:382 +#, c-format +msgid "Saving POP message to %s failed." +msgstr "Việc lưu thư POP vào %s bị lỗi." + +#: ../libbalsa/mailbox_pop3.c:392 +msgid "Transfering POP message to %s failed." +msgstr "Việc truyền thư POP tới %s bị lỗi." + +#: ../libbalsa/mailbox_pop3.c:468 +#, c-format +msgid "" +"POP3 mailbox %s temp file error:\n" +"%s" +msgstr "" +"Lỗi tập tin tạm của hộp thư POP3 %s:\n" +"%s" + +#: ../libbalsa/mailbox_pop3.c:493 +#, c-format +msgid "POP3 mailbox %s error: %s\n" +msgstr "Lỗi hộp thư POP3 %s: %s\n" + +#: ../libbalsa/mailbox_pop3.c:530 +#, c-format +msgid "POP3 message %d oversized: %d kB - skipped." +msgstr "Thư POP3 %d quá lớn: %d KB - bị nhảy qua." + +#: ../libbalsa/mailbox_pop3.c:542 +#, c-format +msgid "POP3 error: cannot open %s for writing." +msgstr "Lỗi POP3: không thể mở %s để ghi." + +#: ../libbalsa/mailbox_pop3.c:550 +#, c-format +msgid "POP3 error: cannot close %s." +msgstr "Lỗ POP3i: không thể đóng %s." + +#: ../libbalsa/mailbox_pop3.c:583 +#, c-format +msgid "POP3 error: %s." +msgstr "Lỗi POP3: %s" + +#: ../libbalsa/message.c:576 ../libbalsa/message.c:616 +#, c-format +msgid "Mailbox (%s) is readonly: cannot change flags." +msgstr "Hộp thư (%s) chỉ cho phép đọc nên không thể thay đổi cờ." + +#: ../libbalsa/misc.c:1248 +msgid "west european" +msgstr "Tây Âu" + +#: ../libbalsa/misc.c:1250 ../libbalsa/misc.c:1284 +msgid "east european" +msgstr "Tây Đông" + +#: ../libbalsa/misc.c:1252 +msgid "south european" +msgstr "Nam Âu" + +#: ../libbalsa/misc.c:1254 +msgid "north european" +msgstr "Bắc Âu" + +#: ../libbalsa/misc.c:1256 ../libbalsa/misc.c:1286 +msgid "cyrillic" +msgstr "Ki-rin" + +#: ../libbalsa/misc.c:1258 ../libbalsa/misc.c:1292 +msgid "arabic" +msgstr "Ả Rập" + +#: ../libbalsa/misc.c:1260 ../libbalsa/misc.c:1288 +msgid "greek" +msgstr "Hy-lạp" + +#: ../console-keymaps-acorn.templates:3 ../console-keymaps-at.templates:3 +msgid "hebrew" +msgstr "Do-thái" + +#: ../libbalsa/misc.c:1264 +msgid "turkish" +msgstr "Thổ-nhĩ-kỳ" + +#: ../libbalsa/misc.c:1266 +msgid "nordic" +msgstr "Xcăng-đi-na-vi" + +#: ../libbalsa/misc.c:1268 +msgid "thai" +msgstr "Thái" + +#: ../libbalsa/misc.c:1270 ../libbalsa/misc.c:1294 +msgid "baltic" +msgstr "Ban-tích" + +#: ../libbalsa/misc.c:1272 +msgid "celtic" +msgstr "Xen-tơ" + +#: ../libbalsa/misc.c:1274 +msgid "west europe (euro)" +msgstr "Đông Âu (€)" + +#: ../libbalsa/misc.c:1276 +msgid "russian" +msgstr "Nga" + +#: ../libbalsa/misc.c:1278 +msgid "ukranian" +msgstr "U-cợ-rainh" + +#: ../libbalsa/misc.c:1280 +msgid "japanese" +msgstr "Nhật-bản" + +#: ../libbalsa/misc.c:1282 +msgid "korean" +msgstr "Hàn Quốc" + +#: ../libbalsa/misc.c:1900 +msgid "Timeout exceeded while attempting fcntl lock!" +msgstr "• Quá thời khi cố khoá fcntl. •" + +#: ../libbalsa/misc.c:1907 +#, c-format +msgid "Waiting for fcntl lock... %d" +msgstr "Đang đợi khoá fcntl... %d" + +#: ../libbalsa/misc.c:1935 +msgid "Timeout exceeded while attempting flock lock!" +msgstr "• Quá thời khi cố khoá flock. •" + +#: ../libbalsa/misc.c:1942 +#, c-format +msgid "Waiting for flock attempt... %d" +msgstr "Đang đợi cố flock... %d" + +#: ../libbalsa/misc.c:2098 +msgid "No image data" +msgstr "Không có dữ liệu ảnh" + +#: ../libbalsa/misc.c:2132 +msgid "Invalid input format" +msgstr "Dạng thức nhập không hợp lệ" + +#: ../libbalsa/misc.c:2136 +msgid "Internal buffer overrun" +msgstr "Tràn qua bộ đệm nội bộ" + +#. Translators: please do not translate Face. +#: ../libbalsa/misc.c:2153 +msgid "Bad X-Face data" +msgstr "Dữ liệu X-Face sai" + +#: ../libbalsa/rfc3156.c:86 +#, c-format +msgid "Gpgme has been compiled without support for protocol %s." +msgstr "Gpgme đã được biên dịch không có hỗ trợ giao thức %s." + +#: ../libbalsa/rfc3156.c:91 +#, c-format +msgid "Crypto engine %s is not installed properly." +msgstr "Cơ chế mật mã %s không được cài đặt đúng." + +#: ../libbalsa/rfc3156.c:96 +#, c-format +msgid "" +"Crypto engine %s version %s is installed, but at least version %s is " +"required." +msgstr "" +"Cơ chế mật mã %s phiên bản %s được cài đặt, còn cần thiết ít nhất phiên bản %" +"s." + +#: ../libbalsa/rfc3156.c:103 +#, c-format +msgid "Unknown problem with engine for protocol %s." +msgstr "Gặp lỗi lạ với cơ chế cho giao thức %s." + +#: ../libbalsa/rfc3156.c:108 +#, c-format +msgid "%s: could not retreive crypto engine information: %s." +msgstr "%s: không thể lấy thông tin cơ chế mật mã: %s." + +#: ../libbalsa/rfc3156.c:112 +#, c-format +msgid "" +"\n" +"Disable support for protocol %s." +msgstr "" +"\n" +"Tắt hỗ trợ giao thức %s." + +#: ../libbalsa/rfc3156.c:254 ../libbalsa/rfc3156.c:259 +#: ../libbalsa/rfc3156.c:346 ../libbalsa/rfc3156.c:351 +#: ../libbalsa/rfc3156.c:515 ../libbalsa/rfc3156.c:520 +#: ../libbalsa/rfc3156.c:625 ../libbalsa/rfc3156.c:630 +#: ../libbalsa/rfc3156.c:739 ../libbalsa/rfc3156.c:744 +#: ../libbalsa/rfc3156.c:852 ../libbalsa/rfc3156.c:857 +#: ../libbalsa/rfc3156.c:932 ../libbalsa/rfc3156.c:937 +msgid "creating a gpgme context failed" +msgstr "việc tạo ngữ cảnh GPGME bị lỗi" + +#: ../libbalsa/rfc3156.c:272 ../libbalsa/rfc3156.c:757 +msgid "Enter passphrase to unlock the secret key for signing" +msgstr "Nhập cụm từ mật khẩu để mở khoá khoá bí mật để ký tên." + +#: ../libbalsa/rfc3156.c:293 ../libbalsa/rfc3156.c:297 +#: ../libbalsa/rfc3156.c:789 +msgid "signing failed" +msgstr "việc ký tên bị lỗi" + +#: ../libbalsa/rfc3156.c:400 ../libbalsa/rfc3156.c:405 +#: ../libbalsa/rfc3156.c:792 +msgid "encryption failed" +msgstr "việc mật mã bị lỗi" + +#: ../libbalsa/rfc3156.c:548 ../libbalsa/rfc3156.c:553 +#: ../libbalsa/rfc3156.c:868 ../libbalsa/rfc3156.c:874 +msgid "signature verification failed" +msgstr "việc thẩm tra chữ ký bị lỗi" + +#: ../libbalsa/rfc3156.c:642 ../libbalsa/rfc3156.c:948 +msgid "Enter passphrase to decrypt message" +msgstr "Hãy gõ lại cụm từ mật khẩu để giải mật mã thư." + +#: ../libbalsa/rfc3156.c:675 ../libbalsa/rfc3156.c:680 +msgid "decryption failed" +msgstr "việc giải mật mã bị lỗi" + +#: ../libbalsa/rfc3156.c:785 ../libbalsa/rfc3156.c:799 +msgid "signing and encryption failed" +msgstr "việc ký tên và mật mã bị lỗi" + +#: ../libbalsa/rfc3156.c:802 +#, c-format +msgid "signing failed: %s" +msgstr "việc ký tên bị lỗi: %s" + +#: ../libbalsa/rfc3156.c:805 +#, c-format +msgid "encryption failed: %s" +msgstr "việc mật mã bị lỗi: %s" + +#: ../libbalsa/rfc3156.c:957 ../libbalsa/rfc3156.c:964 +msgid "decryption and signature verification failed" +msgstr "việc giải mật mã và thẩm tra chữ ký bị lỗi" + +#: ../libbalsa/rfc3156.c:995 +msgid "The signature is valid." +msgstr "Chữ ký là hợp lệ." + +#: ../libbalsa/rfc3156.c:997 +msgid "The signature is valid but expired." +msgstr "Chữ ký là hợp lệ còn đã hết hạn." + +#: ../libbalsa/rfc3156.c:1000 +msgid "" +"The signature is valid but the key used to verify the signature has expired." +msgstr "Chữ ký là hợp lệ còn khoá dùng để thẩm tra chữ ký đã hết hạn dùng." + +#: ../libbalsa/rfc3156.c:1003 +msgid "" +"The signature is valid but the key used to verify the signature has been " +"revoked." +msgstr "Chữ ký là hợp lệ còn khoá dùng để thẩm tra chữ ký đã bị hủy bỏ." + +#: ../libbalsa/rfc3156.c:1006 +msgid "The signature is invalid." +msgstr "Chữ ký không hợp lệ." + +#: ../libbalsa/rfc3156.c:1009 +msgid "The signature could not be verified due to a missing key." +msgstr "Không thể thẩm tra chữ ký do khoá còn thiếu." + +#: ../libbalsa/rfc3156.c:1011 +msgid "This part is not a real PGP signature." +msgstr "Phần này không phải là chữ ký PGP thật." + +#: ../libbalsa/rfc3156.c:1014 +msgid "The signature could not be verified due to an invalid crypto engine." +msgstr "Không thể thẩm tra chữ ký do cơ chế mật mã không hợp lệ." + +#: ../libbalsa/rfc3156.c:1017 ../libbalsa/rfc3156.c:1757 +msgid "GnuPG is rebuilding the trust database and is currently unavailable." +msgstr "" +"GnuPG đang xây dụng lại cơ sở dữ liệu tin cây, và hiện thời không sẵn sàng." + +#: ../libbalsa/rfc3156.c:1020 +msgid "An error prevented the signature verification." +msgstr "Một lỗi đã ngăn cản thẩm tra chữ ký." + +#: ../libbalsa/rfc3156.c:1028 +msgid "The user ID is of unknown validity." +msgstr "ID người dùng có độ hợp lệ không rõ." + +#: ../libbalsa/rfc3156.c:1030 +msgid "The validity of the user ID is undefined." +msgstr "ID người dùng có độ hợp lệ chưa định nghĩa." + +#: ../libbalsa/rfc3156.c:1032 +msgid "The user ID is never valid." +msgstr "ID người dùng không bao giờ hợp lệ." + +#: ../libbalsa/rfc3156.c:1034 +msgid "The user ID is marginally valid." +msgstr "ID người dùng là hợp lệ sát giới hạn." + +#: ../libbalsa/rfc3156.c:1036 +msgid "The user ID is fully valid." +msgstr "ID người dùng là hợp lệ đầy đủ." + +#: ../libbalsa/rfc3156.c:1038 +msgid "The user ID is ultimately valid." +msgstr "ID người dùng là hợp lệ sau cùng." + +#: ../libbalsa/rfc3156.c:1040 ../libbalsa/rfc3156.c:1285 +msgid "bad validity" +msgstr "độ hợp lệ sai" + +#: ../libbalsa/rfc3156.c:1048 +msgid "PGP signature: " +msgstr "Chữ ký PGP: " + +#: ../libbalsa/rfc3156.c:1050 +msgid "S/MIME signature: " +msgstr "Chữ ký S/MIME: " + +#: ../libbalsa/rfc3156.c:1052 +msgid "(unknown protocol) " +msgstr "(giao thức không rõ) " + +#: ../libbalsa/rfc3156.c:1071 +#, c-format +msgid "" +"\n" +"User ID: %s" +msgstr "" +"\n" +"ID người dùng: %s" + +#: ../libbalsa/rfc3156.c:1074 +#, c-format +msgid "" +"\n" +"Signed by: %s" +msgstr "" +"\n" +"Ký tên do : %s" + +#: ../libbalsa/rfc3156.c:1078 +#, c-format +msgid "" +"\n" +"Mail address: %s" +msgstr "" +"\n" +"Địa chỉ thư : %s" + +#: ../libbalsa/rfc3156.c:1083 +#, c-format +msgid "" +"\n" +"Signed on: %s" +msgstr "" +"\n" +"Ký tên vào : %s" + +#: ../libbalsa/rfc3156.c:1085 +#, c-format +msgid "" +"\n" +"User ID validity: %s" +msgstr "" +"\n" +"Độ hợp lệ ID người dùng: %s" + +#: ../libbalsa/rfc3156.c:1089 +#, c-format +msgid "" +"\n" +"Key owner trust: %s" +msgstr "" +"\n" +"Độ tin cây chủ khoá: %s" + +#: ../libbalsa/rfc3156.c:1093 +#, c-format +msgid "" +"\n" +"Key fingerprint: %s" +msgstr "" +"\n" +"Vân tay khoá: %s" + +#: ../libbalsa/rfc3156.c:1098 +#, c-format +msgid "" +"\n" +"Subkey created on: %s" +msgstr "" +"\n" +"Khoá phụ tạo vào : %s" + +#: ../libbalsa/rfc3156.c:1103 +#, c-format +msgid "" +"\n" +"Subkey expires on: %s" +msgstr "" +"\n" +"Khoá phụ hết hạn vào : %s" + +#: ../libbalsa/rfc3156.c:1112 +msgid " revoked" +msgstr " bị hủy bỏ" + +#: ../libbalsa/rfc3156.c:1117 +msgid " expired" +msgstr " đã hết hạn" + +#: ../libbalsa/rfc3156.c:1122 +msgid " disabled" +msgstr " bị tắt" + +#: ../libbalsa/rfc3156.c:1127 +msgid " invalid" +msgstr " không hợp lệ" + +#: ../libbalsa/rfc3156.c:1130 +#, c-format +msgid "" +"\n" +"Subkey attributes:%s" +msgstr "" +"\n" +"Thuộc tính khoá phụ : %s" + +#: ../libbalsa/rfc3156.c:1132 +#, c-format +msgid "" +"\n" +"Subkey attribute:%s" +msgstr "" +"\n" +"Thuộc tính khoá phụ : %s" + +#: ../libbalsa/rfc3156.c:1138 +#, c-format +msgid "" +"\n" +"Issuer name: %s" +msgstr "" +"\n" +"Tên nhà phát hành: %s" + +#: ../libbalsa/rfc3156.c:1142 +#, c-format +msgid "" +"\n" +"Issuer serial number: %s" +msgstr "" +"\n" +"Số sản xuất của nhà phát hành: %s" + +#: ../libbalsa/rfc3156.c:1145 +#, c-format +msgid "" +"\n" +"Chain ID: %s" +msgstr "" +"\n" +"ID dây: %s" + +#: ../libbalsa/rfc3156.c:1192 +#, c-format +msgid "Could not launch %s to get the public key %s." +msgstr "Không thể khởi chạy %s để lấy khoá công %s." + +#: ../libbalsa/rfc3156.c:1242 +#, c-format +msgid "" +"Running gpg failed with return value %d:\n" +"%s" +msgstr "" +"Việc chạy GPG bị lỗi với giá trị gởi trả %d:\n" +"%s" + +#: ../libbalsa/rfc3156.c:1249 +#, c-format +msgid "" +"Running gpg successful:\n" +"%s" +msgstr "" +"Việc chạy GPG thành công:\n" +"%s" + +#: ../ui/mlview-validation-report.glade.h:10 ../src/glade-gtk.c:3636 +#: src/friends.c:364 libexif/olympus/mnote-olympus-entry.c:381 +#: ../bin/ical-dump.c:71 ../bin/ical-dump.c:97 ../src/orca/rolenames.py:484 +#: ../freedesktop.org.xml.in.h:385 +msgid "unknown" +msgstr "không rõ" + +#: ../libbalsa/rfc3156.c:1275 avr-dis.c:112 avr-dis.c:122 +#, c-format +msgid "undefined" +msgstr "chưa định nghĩa" + +#: ../libbalsa/rfc3156.c:1277 +msgid "never" +msgstr "không bao giờ" + +#: ../libbalsa/rfc3156.c:1279 +msgid "marginal" +msgstr "sát giới hạn" + +#: src/www.c:436 src/www.c:559 src/www.c:560 src/www.c:561 src/www.c:562 +msgid "full" +msgstr "đầy đủ" + +#: ../libbalsa/rfc3156.c:1283 +msgid "ultimate" +msgstr "cuối cùng" + +#: ../libbalsa/rfc3156.c:1346 restore_gui.c:295 +msgid "User ID" +msgstr "ID người dùng" + +#: ../libbalsa/rfc3156.c:1346 +msgid "Key ID" +msgstr "ID khoá" + +#: ../libgnomedb/gnome-db-selector.c:500 ../libgnomedb/sel-onetable.c:209 +#: app/envelope-box.c:878 +msgid "Length" +msgstr "Dài" + +# Name: don't translate / Tên: đừng dịch +#: ../libbalsa/rfc3156.c:1346 +msgid "Validity" +msgstr "Hợp lệ" + +#. FIXME: create dialog according to the Gnome HIG +#: ../libbalsa/rfc3156.c:1390 +msgid "Select key" +msgstr "Chọn khoá" + +#: ../libbalsa/rfc3156.c:1401 +#, c-format +msgid "Select the private key for the signer %s" +msgstr "Chọn khoá riêng cho người ký tên %s" + +#: ../libbalsa/rfc3156.c:1405 +#, c-format +msgid "Select the public key for the recipient %s" +msgstr "Chọn khoá công cho người nhận %s" + +#: ../libbalsa/rfc3156.c:1536 +#, c-format +msgid "Insufficient trust for recipient %s" +msgstr "Không đủ tin cây cho người nhận %s" + +#: ../libbalsa/rfc3156.c:1538 +#, c-format +msgid "The validity of the key with user ID \"%s\" is \"%s\"." +msgstr "Độ hợp lệ của khoá có ID người dùng « %s » là « %s »." + +#: ../libbalsa/rfc3156.c:1549 +msgid "Use this key anyway?" +msgstr "Vẫn dùng khoá này không?" + +#: ../libbalsa/rfc3156.c:1758 +msgid "Try again later." +msgstr "Thử lại sau." + +#: ../libbalsa/send.c:243 ../libbalsa/send.c:250 +msgid "Sending Mail..." +msgstr "Đang gởi thư..." + +#: ../libbalsa/send.c:246 ../src/main-window.c:2790 +#: ../bonobo/bonobo-ui-config-widget.c:282 +msgid "_Hide" +msgstr "Ẩ_n" + +#: ../libbalsa/send.c:453 +#, c-format +msgid "Copying message to outbox failed: %s" +msgstr "Việc sao chép thư vào hộp Thư Đi bị lỗi: %s" + +#: ../libbalsa/send.c:882 +#, c-format +msgid "" +"Could not send the message to %s:\n" +"%d: %s\n" +"Message left in your outbox.\n" +msgstr "" +"Không thể gởi thư cho %s:\n" +"%d: %s\n" +"Thư còn lại trong hộp Thư Đi của bạn.\n" + +#: ../libbalsa/send.c:926 +#, c-format +msgid "Saving sent message to %s failed: %s" +msgstr "Việc lưu thư đã gởi vào %s bị lỗi: %s" + +#: ../libbalsa/send.c:946 +#, c-format +msgid "" +"Relaying refused:\n" +"%d: %s\n" +"Message left in your outbox.\n" +msgstr "" +"Việc tiếp lại bị từ chối:\n" +"%d: %s\n" +"Thư còn lại trong hộp Thư Đi của bạn.\n" + +#: ../libbalsa/send.c:956 +msgid "" +"Message submission problem, placing it into your outbox.\n" +"System will attempt to resubmit the message until you delete it." +msgstr "" +"Lỗi đệ trình thư, bỏ nó vào trong hộp Thư Đi.\n" +"Hệ thống sẽ cố đệ trình lại thư cho tới khi bạn xoá nó." + +#: ../libbalsa/send.c:986 +msgid "Connected to MTA" +msgstr "Đã kết nối đến MTA" + +#. status code, mailbox +#: ../libbalsa/send.c:995 +#, c-format +msgid "From: %d <%s>" +msgstr "Từ : %d <%s>" + +#. mailbox, status code, status text +#: ../libbalsa/send.c:999 +#, c-format +msgid "From %s: %d %s" +msgstr "Từ %s: %d %s" + +#. status code, mailbox +#: ../libbalsa/send.c:1010 +#, c-format +msgid "To: %d <%s>" +msgstr "Cho : %d <%s>" + +#. mailbox, status code, status text +#: ../libbalsa/send.c:1014 +#, c-format +msgid "To %s: %d %s" +msgstr "Cho %s: %d %s" + +#: ../libbalsa/send.c:1052 ../src/netstatus-util.c:152 ../src/netapplet.c:152 +msgid "Disconnected" +msgstr "Kết nối bị ngắt" + +#: ../libbalsa/send.c:1325 +msgid "" +"SMTP server refused connection.\n" +"Check your internet connection." +msgstr "" +"Máy phục vụ SMTP đã từ chối kết nối.\n" +"Hãy kiểm tra xem có kết nối nội bộ đúng." + +#: ../libbalsa/send.c:1330 +msgid "" +"SMTP server cannot be reached.\n" +"Check your internet connection." +msgstr "" +"Không thể tới máy phục vụ SMTP.\n" +"Hãy kiểm tra xem có kết nối Mạng đúng." + +#: ../libbalsa/send.c:1339 +msgid "Message left in Outbox (try again later)" +msgstr "Thư còn lại trong hộp Thư Đi (thử lại sau)." + +#: ../libbalsa/send.c:1343 +#, c-format +msgid "" +"SMTP server problem (%d): %s\n" +"Message is left in outbox." +msgstr "" +"Vấn đề máy phục vụ SMTP (%d): %s\n" +"Thư còn lại trong hộp Thư Đi." + +#: ../libbalsa/send.c:1648 +#, c-format +msgid "Cannot determine charset for text file `%s'; sending as mime type `%s'" +msgstr "" +"Không thể giải quyết bộ ký tự cho tập tin văn bản « %s » nên đang gởi dạng " +"kiểu MIME « %s »." + +#: ../libbalsa/send.c:1852 +#, c-format +msgid "Postponing message failed: %s" +msgstr "Việc hoãn thư bị lỗi: %s" + +#: ../libbalsa/send.c:1995 ../libbalsa/send.c:2077 +msgid "This message will not be encrypted for the BCC: recipient(s)." +msgstr "Thư này sẽ không được mật mã cho những người nhận BCC (Bí Mật Cho)." + +#. host, authentication method +#: ../libbalsa/server.c:366 +#, c-format +msgid "Logging in to %s using %s" +msgstr "Đang đăng nhập vào %s bằng %s..." + +#: ../mail/em-account-editor.c:766 ../src/personal_info.c:267 +#: ../widgets/gtk+.xml.in.h:125 +msgid "Never" +msgstr "Không bao giờ" + +#: ../libbalsa/smtp-server.c:371 ../src/mailbox-conf.c:205 +#: ../src/mailbox-conf.c:1387 +msgid "If Possible" +msgstr "Nếu có thể" + +#: ../libbalsa/smtp-server.c:372 ../src/mailbox-conf.c:206 +#: dselect/pkgdisplay.cc:60 ../src/personal_info.c:299 +msgid "Required" +msgstr "Cần thiết" + +#: ../libbalsa/smtp-server.c:409 ../src/folder-conf.c:104 +#, c-format +msgid "Error displaying %s: %s\n" +msgstr "Gặp lỗi khi hiển thị %s: %s\n" + +#: ../libbalsa/smtp-server.c:525 +msgid "SMTP Server" +msgstr "Máy phục vụ SMTP" + +#: ../libbalsa/smtp-server.c:547 +msgid "_Descriptive Name:" +msgstr "Tên _mô tả:" + +#: ../src/baobab-remote-connect-dialog.c:448 +msgid "_Server:" +msgstr "Máy _phục vụ :" + +#: ../libbalsa/smtp-server.c:561 ../src/baobab-remote-connect-dialog.c:540 +msgid "_User Name:" +msgstr "Tên _ngươì dùng:" + +#: ../libbalsa/smtp-server.c:568 +msgid "_Pass Phrase:" +msgstr "Cụm từ _mật khẩu :" + +#: ../libbalsa/smtp-server.c:576 ../src/mailbox-conf.c:219 +msgid "Use _TLS:" +msgstr "Dùng _TLS:" + +#: ../libbalsa/smtp-server.c:582 +msgid "C_ertificate Pass Phrase:" +msgstr "_Cụm từ mật khẩu chứng nhận:" + +#: ../libbalsa/smtp-server.c:594 +msgid "Sp_lit message larger than" +msgstr "Chia _tách thư lớn hơn" + +#: ../libbalsa/smtp-server.c:600 ../src/pref-manager.c:1697 +#: ../src/smart-playlist-dialog.c:185 +msgid "MB" +msgstr "MB" + +#: ../ui/connect.glade.h:9 ../gmedia_format/gmedia_format.c:373 +msgid "_Close" +msgstr "_Đóng" + +#: ../libbalsa/source-viewer.c:48 ../ec-job-list.c:783 +msgid "Close the window" +msgstr "Đóng cửa sổ" + +#: ../libbalsa/source-viewer.c:49 ../plug-ins/common/curve_bend.c:1426 +#: src/floatwin.cpp:899 app/menubar.c:451 app/menubar.c:462 app/menubar.c:518 +#: app/menubar.c:549 +msgid "_Copy" +msgstr "_Chép" + +#: ../libbalsa/source-viewer.c:50 +msgid "Copy text" +msgstr "Chép phần" + +#: ../libbalsa/source-viewer.c:51 ../src/main-window.c:661 +msgid "_Select Text" +msgstr "_Lựa chọn phần" + +#: ../libbalsa/source-viewer.c:52 ../src/main-window.c:662 +msgid "Select entire mail" +msgstr "Chọn toàn bộ thư" + +#: ../libbalsa/source-viewer.c:57 +msgid "_Escape Special Characters" +msgstr "T_hoát ký tự đặc biệt" + +#: ../libbalsa/source-viewer.c:58 +msgid "Escape special and non-ASCII characters" +msgstr "Thoát mọi ký tự đặc biệt và khác ASCII" + +#: ../libbalsa/source-viewer.c:154 +msgid "Mailbox closed" +msgstr "Hộp thư được đóng" + +#: ../libbalsa/source-viewer.c:257 ../ui/evolution-mail-message.xml.h:57 +#: ../ui/evolution-mail-message.xml.h:56 +msgid "Message Source" +msgstr "Mã nguồn thư" + +#: ../libinit_balsa/balsa-druid-page-defclient.c:58 +msgid "Use balsa as default email client?" +msgstr "Đặt trình balsa là ứng dụng khách thư điện tử mặc định không?" + +#: ../libinit_balsa/balsa-druid-page-defclient.c:62 ../gtk/gtkstock.c:412 +msgid "_Yes" +msgstr "_Có" + +#: ../libinit_balsa/balsa-druid-page-defclient.c:64 ../gtk/gtkstock.c:387 +msgid "_No" +msgstr "_Không" + +#: ../libinit_balsa/balsa-druid-page-defclient.c:100 +msgid "Default Client" +msgstr "Ứng dụng khách mặc định" + +#: ../libinit_balsa/balsa-druid-page-directory.c:43 +#: ../storage/exchange-delegates.glade.h:14 +msgid "_Inbox:" +msgstr "Thư _Đến:" + +#: ../libinit_balsa/balsa-druid-page-directory.c:43 +msgid "_Outbox:" +msgstr "Thư _Đi:" + +#: ../libinit_balsa/balsa-druid-page-directory.c:43 +msgid "_Sentbox:" +msgstr "Đã _Gởi:" + +#: ../libinit_balsa/balsa-druid-page-directory.c:43 +msgid "_Draftbox:" +msgstr "Nhá_p:" + +#: ../libinit_balsa/balsa-druid-page-directory.c:44 +msgid "_Trash:" +msgstr "_Rác:" + +#: ../libinit_balsa/balsa-druid-page-directory.c:71 +#, c-format +msgid "" +"The pathname \"%s\" must be specified canonically -- it must start with a " +"'/'." +msgstr "" +"Phải chỉ định đường dẫn « %s » theo đúng quy tắc — nó phải bắt đầu bằng sổ " +"chéo « / »." + +#: ../libinit_balsa/balsa-druid-page-directory.c:135 +#, c-format +msgid "The mailbox \"%s\" does not appear to be valid." +msgstr "Hộp thư « %s » có vẻ không phải là hợp lệ." + +#: ../libinit_balsa/balsa-druid-page-directory.c:188 +msgid "" +"Please verify the locations of your default mail files.\n" +"These will be created if necessary." +msgstr "" +"Hãy kiểm tra lại vị trí các tập tin thư mặc định.\n" +"Chúng sẽ được tạo nếu cần thiết." + +#: ../libinit_balsa/balsa-druid-page-directory.c:246 +msgid "Mail Files" +msgstr "Tập tin thư" + +#: ../libinit_balsa/balsa-druid-page-directory.c:349 +#: ../libinit_balsa/balsa-druid-page-directory.c:383 +#, c-format +msgid "" +"Problem Creating Mailboxes\n" +"%s" +msgstr "" +"Gặp khó khăn tạo hộp thư\n" +"%s" + +#: ../libinit_balsa/balsa-druid-page-finish.c:41 +msgid "" +"You've successfully set up Balsa. Have fun!\n" +" -- The Balsa development team" +msgstr "" +"Bạn đã thiết lập xong Balsa. Hãy tận hưởng!\n" +" — Nhóm phát triển Balsa" + +#: ../libinit_balsa/balsa-druid-page-finish.c:47 +msgid "All Done!" +msgstr "Đã xong!" + +#: ../libinit_balsa/balsa-druid-page-finish.c:77 +msgid "GnomeCard Address Book" +msgstr "Sổ địa chỉ Thẻ Gnome" + +#: ../libinit_balsa/balsa-druid-page-finish.c:84 +#: ../libinit_balsa/balsa-druid-page-finish.c:92 ../src/ab-window.c:226 +#: ../libedataserverui/e-name-selector-dialog.glade.h:5 jpilot.c:2751 +msgid "Address Book" +msgstr "Sổ địa chỉ" + +#: ../libinit_balsa/balsa-druid-page-user.c:71 +msgid "" +"The following settings are also needed (and you can find them later, if need " +"be, in the Email application in the 'Preferences' and 'Identities' commands " +"on the 'Tools' menu)" +msgstr "" +"Cũng cần những thiết lập theo đây: bạn có thể tìm chúng sau, nếu cần thiết, " +"trong:\n" +"Công cụ → Tùy thích → Thư điện tử ; Công cụ → Thực thể → Thư điện tử." + +#: ../libinit_balsa/balsa-druid-page-user.c:76 +msgid "" +" Whoever provides your email account should be able to give you the " +"following information (if you have a Network Administrator, they may already " +"have set this up for you):" +msgstr "" +"Nhà cung cấp tài khoản thư điện tử cho bạn, cũng có thể cho bạn thông tin " +"theo đây (nếu bạn có quản trị mạng, có lẽ họ đã thiết lập xong):" + +#: ../libinit_balsa/balsa-druid-page-user.c:82 +msgid "Yes, remember it" +msgstr "Có, nhớ đi" + +#: ../libinit_balsa/balsa-druid-page-user.c:82 +msgid "No, type it in every time" +msgstr "Không, gõ mọi lần" + +#: ../libinit_balsa/balsa-druid-page-user.c:114 +msgid "Name of mail server for incoming _mail:" +msgstr "Tên máy _phục vụ thư cho thư gởi đến:" + +#: ../libinit_balsa/balsa-druid-page-user.c:119 +msgid "_Type of mail server:" +msgstr "_Kiểu máy phục vụ thư:" + +#: ../libinit_balsa/balsa-druid-page-user.c:123 +msgid "Connect using _SSL:" +msgstr "Kết nối bằng _SSL:" + +#: ../libinit_balsa/balsa-druid-page-user.c:126 +msgid "Your email _login name:" +msgstr "Tên _đăng nhập thư điện tử :" + +#: ../libinit_balsa/balsa-druid-page-user.c:129 +msgid "Your _password:" +msgstr "_Mật khẩu :" + +#: ../libinit_balsa/balsa-druid-page-user.c:137 +msgid "_SMTP Server:" +msgstr "Máy phục vụ SMTP:" + +#. 2.1 +#: ../libinit_balsa/balsa-druid-page-user.c:142 +msgid "Your real _name:" +msgstr "Tên _thật:" + +#: ../libinit_balsa/balsa-druid-page-user.c:148 +msgid "Your _Email Address, for this email account:" +msgstr "_Địa chỉ thư, cho tài khoản thư này:" + +#: ../libinit_balsa/balsa-druid-page-user.c:153 +msgid "_Remember your password:" +msgstr "_Nhớ mật khẩu :" + +#: ../libinit_balsa/balsa-druid-page-user.c:156 +msgid "_Refer to this account as:" +msgstr "Tên _riêng cho tài khoản này:" + +#: ../libinit_balsa/balsa-druid-page-user.c:164 +msgid "_Local mail directory:" +msgstr "Thư mục thư _cục bộ :" + +#: ../libinit_balsa/balsa-druid-page-user.c:181 +msgid "User Settings" +msgstr "Thiết lập người dùng" + +#: ../libinit_balsa/balsa-druid-page-user.c:333 +#, c-format +msgid "" +"Local Mail Problem\n" +"%s" +msgstr "" +"Vấn đề thư cục bộ\n" +"%s" + +#: ../libinit_balsa/balsa-druid-page-welcome.c:32 +msgid "Welcome to Balsa!" +msgstr "Chúc mừng dùng Balsa!" + +#: ../libinit_balsa/balsa-druid-page-welcome.c:35 +msgid "" +"Before you can send or receive email:\n" +"\n" +"-- either you should already have Internet access and an email account, " +"provided by an Internet Service Provider, and you should have made that " +"Internet connection on your computer\n" +"\n" +"-- or your Network Administrator at your place of work/study/similar may " +"have set up your computer to connect to the network." +msgstr "" +"Bạn có thể gởi hoặc nhận thư nếu :\n" +"hoặc\n" +"• bạn có truy cập đến Mạng và một tài khoản thư điện tử, cung cấp do Nhà " +"Cung Cấp Dịch Vụ Mạng (ISP), và bạn đã kết nối đến Mạng lần đầu tiên\n" +"hoặc\n" +"• Quản trị mạng tại chỗ làm/học/tương tự đã thiết lập máy tính này để kết " +"nối đến mạng." + +#: ../libinit_balsa/balsa-initdruid.c:81 +msgid "" +"This will exit Balsa.\n" +"Do you really want to do this?" +msgstr "" +"Hành động này sẽ thoát khỏi trình Balsa.\n" +"Bạn có chắc muốn thực hiện không?" + +#: ../libinit_balsa/helper.c:66 +#, c-format +msgid "Error loading %s: %s\n" +msgstr "Gặp lỗi khi tải %s: %s\n" + +#: ../libinit_balsa/helper.c:202 +#, c-format +msgid "The path %s must be relative to the filesystem root (start with /)." +msgstr "" +"Đường dẫn %s phải liên quan đến gốc của hệ thống tập tin (bắt đầu bằng sổ " +"chếo « / »)." + +#: ../libinit_balsa/helper.c:215 ../libinit_balsa/helper.c:239 +#, c-format +msgid "Couldn't create a directory: mkdir() failed on pathname \"%s\"." +msgstr "" +"Không thể tạo thư mục: việc « mkdir() » (tạo thư mục) bị lỗi trên tên đường " +"dẫn « %s »." + +#: ../libinit_balsa/helper.c:225 ../libinit_balsa/helper.c:248 +#, c-format +msgid "The file with pathname \"%s\" is not a directory." +msgstr "Tập tin có tên đường dẫn « %s » không phải là thư mục." + +#: ../libinit_balsa/init_balsa.c:48 +msgid "Configure Balsa" +msgstr "Cấu hình Balsa" + +#: ../sounds/balsa.soundlist.in.h:1 +msgid "Balsa E-mail reader" +msgstr "Bộ đọc thư Balsa" + +#: ../sounds/balsa.soundlist.in.h:2 +msgid "New mail notification" +msgstr "Thông báo thư mới" + +#: ../sounds/balsa.soundlist.in.h:3 +msgid "Program startup" +msgstr "Khởi chạy chương trình" + +#: ../src/ab-main.c:199 ../src/ab-window.c:673 +msgid " address book: " +msgstr " sổ địa chỉ: " + +#: ../src/ab-main.c:470 ../src/address-book-config.c:839 +msgid "VCard Address Book (GnomeCard)" +msgstr "Sổ địa chỉ vCard (Thẻ Gnome)" + +#: ../src/ab-main.c:472 ../src/address-book-config.c:845 +msgid "External query (a program)" +msgstr "Truy vấn bên ngoài (chương trình khác)" + +#: ../src/ab-main.c:474 ../src/address-book-config.c:850 +msgid "LDIF Address Book" +msgstr "Sổ địa chỉ LDIF" + +#: ../src/ab-main.c:477 ../src/address-book-config.c:856 +msgid "LDAP Address Book" +msgstr "Sổ địa chỉ LDAP" + +#: ../src/ab-main.c:481 ../src/address-book-config.c:457 +#: ../src/address-book-config.c:863 +msgid "GPE Address Book" +msgstr "Sổ địa chỉ GPE" + +#: ../src/lib/FeedPropertiesDialog.py:96 +#: ../storage/sunone-permissions-dialog.c:559 +#: ../src/glade-editor-property.c:2076 ../src/glade-widget.c:146 +#: src/gpsdrive.c:9462 address_gui.c:2775 KeyRing/keyring.c:1485 +#: ../mimedir/mimedir-attribute.c:131 ../mimedir/mimedir-vcard.c:298 +#: schroot/sbuild-chroot.cc:386 +msgid "Name" +msgstr "Tên" + +#: ../src/ab-main.c:791 +msgid "F_ilter:" +msgstr "_Lọc:" + +#: ../src/ab-window.c:176 ../smime/gui/certificate-manager.c:483 +msgid "E-Mail Address" +msgstr "Địa chỉ thư điện tử" + +#. Entry widget for finding an address +#: ../src/ab-window.c:263 +msgid "_Search for Name:" +msgstr "Tìm _kiếm tên:" + +#: ../src/ab-window.c:323 +msgid "Send-To" +msgstr "Gởi cho" + +#: ../src/ab-window.c:343 +msgid "Run Editor" +msgstr "Chạy bộ soạn thảo" + +#: ../src/ab-window.c:350 +msgid "_Re-Import" +msgstr "Nhập _lại" + +#. mode switching stuff +#: ../src/ab-window.c:360 +msgid "Treat multiple addresses as:" +msgstr "Xử lý nhiều địa chỉ dạng:" + +#: ../src/ab-window.c:364 +msgid "alternative addresses for the same person" +msgstr "địa chỉ xen kẽ cho cùng một người" + +#: ../src/ab-window.c:369 +msgid "a distribution list" +msgstr "danh sách phân phối" + +#: ../src/ab-window.c:702 +#, c-format +msgid "" +"Error opening address book '%s':\n" +"%s" +msgstr "" +"Gặp lỗi khi mở sổ địa chỉ « %s »:\n" +"%s" + +#: ../src/address-book-config.c:165 ../src/address-book-config.c:260 +msgid "Modify Address Book" +msgstr "Sửa đổi Sổ địa chỉ" + +#: ../src/address-book-config.c:169 ../src/address-book-config.c:264 +#: ../addressbook/gui/component/ldap-config.glade.h:12 +#: ../addressbook/gui/component/ldap-config.glade.h:13 +msgid "Add Address Book" +msgstr "Thêm Sổ địa chỉ" + +#. mailbox name +#. may be NULL +#. mailbox name +#: ../src/address-book-config.c:185 ../src/address-book-config.c:299 +#: ../src/address-book-config.c:388 ../src/address-book-config.c:455 +msgid "A_ddress Book Name:" +msgstr "Tên _Sổ địa chỉ:" + +#: ../src/address-book-config.c:190 ../src/address-book-config.c:343 +#: ../src/address-book-config.c:424 ../src/address-book-config.c:460 +msgid "_Expand aliases as you type" +msgstr "_Bung bí danh trong khi gõ" + +#: ../src/address-book-config.c:304 +msgid "Load program location:" +msgstr "Tải vị trí chương trình:" + +#: ../src/address-book-config.c:311 ../src/address-book-config.c:316 +msgid "Select load program for address book" +msgstr "Chọn chương trình tải sổ địa chỉ" + +#: ../src/address-book-config.c:323 +msgid "Save program location:" +msgstr "Lưu vị trí chương trình:" + +#: ../src/address-book-config.c:330 ../src/address-book-config.c:335 +msgid "Select save program for address book" +msgstr "Chọn chương trình lưu sổ địa chỉ" + +#: ../src/address-book-config.c:393 +msgid "_Host Name" +msgstr "Tên _máy" + +#: ../src/address-book-config.c:398 +msgid "Base Domain _Name" +msgstr "Tên miền cơ _bản:" + +#: ../src/address-book-config.c:403 +msgid "_User Name (Bind DN)" +msgstr "Tên _ngươì dùng (Tên miền Bind):" + +#: ../src/address-book-config.c:408 +msgid "_Password" +msgstr "_Mật khẩu" + +#: ../src/address-book-config.c:414 +msgid "_User Address Book DN" +msgstr "Tên miền của Sổ địa chỉ ng_ười dùng" + +#: ../src/address-book-config.c:420 +msgid "Enable _TLS" +msgstr "Bật _TLS" + +#: ../src/address-book-config.c:478 ../src/main-window.c:4098 +#, c-format +msgid "Error displaying help: %s\n" +msgstr "Gặp lỗi khi hiển thị trợ giúp: %s\n" + +#: ../src/address-book-config.c:579 +msgid "No path found. Do you want to give one?" +msgstr "Không tìm thấy đường dẫn: bạn có muốn gõ nó vậy?" + +#: ../src/address-book-config.c:585 +#, c-format +msgid "The address book file path \"%s\" is not correct. %s" +msgstr "Đường dẫn của tập tin sổ địa chỉ « %s » là không đúng. %s" + +#: ../src/address-book-config.c:588 +#, c-format +msgid "The load program path \"%s\" is not correct. %s" +msgstr "Đường dẫn của chương trình tải « %s » là không đúng. %s" + +#: ../src/address-book-config.c:591 +#, c-format +msgid "The save program path \"%s\" is not correct. %s" +msgstr "Đường dẫn của chương trình lưu « %s » là không đúng. %s" + +#: ../src/address-book-config.c:594 +#, c-format +msgid "The path \"%s\" is not correct. %s" +msgstr "Đường dẫn « %s » là không đúng. %s" + +#: ../src/address-book-config.c:597 +msgid "Do you want to correct the path?" +msgstr "Bạn có muốn sửa đường dẫn đó chứ?" + +#: ../src/balsa-app.c:69 +#, c-format +msgid "" +"Opening remote mailbox %s.\n" +"The _password for %s@%s:" +msgstr "" +"Đang mở hộp thư ở xa %s.\n" +"_Mật khẩu cho %s@%s:" + +#: ../src/balsa-app.c:74 +#, c-format +msgid "Mailbox _password for %s@%s:" +msgstr "_Mật khẩu hộp thư cho %s@%s:" + +#: ../src/balsa-app.c:77 ../src/gnomesu-auth-dialog.c:111 +msgid "Password needed" +msgstr "Cần mật khẩu" + +#: ../src/drivel.glade.h:78 +msgid "_Remember password" +msgstr "_Nhớ mật khẩu" + +#: ../src/balsa-app.c:540 +#, c-format +msgid "Couldn't open mailbox \"%s\"" +msgstr "Không thể mở hộp thư « %s »." + +#: ../pan/text.c:707 +msgid "From" +msgstr "Từ" + +#: ../pan/prefs.c:1393 ../pan/prefs.c:1633 ../pan/text.c:703 +msgid "Subject" +msgstr "Chủ đề" + +#: ../Pyblio/GnomeUI/Fields.py:43 src/dictmanagedlg.cpp:525 +#: datebook_gui.c:4767 +msgid "Date" +msgstr "Ngày" + +#: ../providers/odbc/gda-odbc-provider.c:976 src/prefsdlg.cpp:68 +#: ../widgets/gtk+.xml.in.h:162 +msgid "Size" +msgstr "Cỡ" + +#: ../src/balsa-index.c:1043 +#, c-format +msgid "Opening mailbox %s. Please wait..." +msgstr "Đang mở hộp thư %s. Hãy đời..." + +#: ../src/balsa-index.c:1086 src/fe-gtk/dccgui.c:712 src/gpsdrive.c:3665 +#: src/gpsdrive.c:3820 src/gpsdrive.c:6737 src/gpsdrive.c:8505 +#: src/gpsdrive.c:8996 +#, fuzzy +msgid "To" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Cho\n" +"#-#-#-#-# gpsdrive-2.08pre6.vi.po (gpsdrive-2.08pre6) #-#-#-#-#\n" +"Đến" + +#: ../src/balsa-index.c:1684 +#, c-format +msgid "Move to Trash failed: %s" +msgstr "Việc Chuyển vào Rác bị lỗi: %s" + +#. R +#: ../src/balsa-index.c:1838 ../src/main-window.c:584 +#: ../src/main-window.c:1047 ../src/message-window.c:148 +msgid "_Reply..." +msgstr "T_rả lời..." + +#: ../src/balsa-index.c:1840 +msgid "Reply To _All..." +msgstr "Trả lời _mọi người..." + +#: ../src/balsa-index.c:1842 +msgid "Reply To _Group..." +msgstr "Trả lời _nhóm..." + +#: ../src/balsa-index.c:1844 +msgid "_Forward Attached..." +msgstr "_Chuyển tiếp đồ đính kèm..." + +#: ../src/balsa-index.c:1846 +msgid "Forward _Inline..." +msgstr "Chuyển tiếp trực t_iếp..." + +#: ../src/balsa-index.c:1848 ../src/main-window.c:623 +msgid "_Pipe through..." +msgstr "Gởi _qua ống dẫn..." + +#: ../src/balsa-index.c:1850 ../src/main-window.c:682 +#: ../src/main-window.c:1031 +msgid "_Store Address..." +msgstr "_Lưu địa chỉ..." + +#: ../ui/muds.glade.h:48 ../gmedia_slice/interface.c:190 +#: ../storage/sunone-folder-tree.c:1281 +msgid "_Delete" +msgstr "_Xoá bỏ" + +#: ../src/balsa-index.c:1870 ../gtk/gtkstock.c:409 +msgid "_Undelete" +msgstr "_Phục hồi" + +#: ../src/balsa-index.c:1875 +msgid "Move To _Trash" +msgstr "Chuyển vào _Rác" + +#: ../src/balsa-index.c:1879 +msgid "T_oggle" +msgstr "_Bật tắt" + +#: ../src/balsa-index.c:1883 ../src/main-window.c:331 ../src/main-window.c:360 +msgid "_Flagged" +msgstr "Đã đặt cờ" + +#: ../src/balsa-index.c:1886 ../ui/evolution-mail-message.xml.h:128 +#: ../ui/evolution-mail-message.xml.h:122 +msgid "_Unread" +msgstr "Ch_ưa đọc" + +#: ../src/balsa-index.c:1894 +msgid "_Move to" +msgstr "_Chuyển vào" + +#: ../src/balsa-index.c:1902 ui/galeon-ui.xml.in.h:205 +#: ../display/Display.py:88 +msgid "_View Source" +msgstr "_Xem mã nguồn" + +#: ../src/balsa-index.c:2155 +#, c-format +msgid "Failed to copy messages to mailbox \"%s\": %s" +msgstr "Việc sao chép thư vào hộp thư « %s » bị lỗi: %s" + +#: ../src/balsa-index.c:2156 +#, c-format +msgid "Failed to copy message to mailbox \"%s\": %s" +msgstr "Việc sao chép thư vào hộp thư « %s » bị lỗi: %s" + +#: ../src/balsa-index.c:2168 +#, c-format +msgid "Copied to \"%s\"." +msgstr "Đã sao chép vào « %s »." + +#: ../src/balsa-index.c:2169 +#, c-format +msgid "Moved to \"%s\"." +msgstr "Đã chuyển vào « %s »." + +#: ../src/balsa-index.c:2288 +#, c-format +msgid "Committing mailbox %s failed." +msgstr "Việc gài vào hộp thư %s bị lỗi." + +#: ../src/balsa-index.c:2461 +msgid "Pipe message through a program" +msgstr "Gởi thư qua ống dẫn đến chương trình" + +#: ../src/balsa-index.c:2464 ../src/file-manager/fm-directory-view.c:4020 +msgid "_Run" +msgstr "_Chạy" + +#: ../src/balsa-index.c:2473 +msgid "Specify the program to run:" +msgstr "Xác định chương trình cần chạy:" + +#: ../src/balsa-mblist.c:301 src/gbiff2.strings:35 +msgid "Mailbox" +msgstr "Hộp thư" + +#: ../src/balsa-mblist.c:1053 ../src/balsa-mblist.c:1089 +msgid "Failed to find mailbox" +msgstr "Việc tìm hộp thư bị lỗi." + +#: ../src/balsa-mblist.c:1663 ../src/ephy-encoding-menu.c:350 +msgid "_Other..." +msgstr "_Khác..." + +#: ../src/balsa-mblist.c:1731 ../data/glade/goobox.glade.h:10 +#: ../src/dlg-extract.c:263 utils/gul-download.c:68 +msgid "Choose destination folder" +msgstr "Chọn thư mục đích" + +#: ../src/balsa-mblist.c:1954 ../src/balsa-mblist.c:1957 +#: ../gtk/gtkfilechooserbutton.c:1531 ../app/display/gimpstatusbar.c:452 +#: app/gui.c:2084 +msgid "Other..." +msgstr "Khác..." + +#: ../src/balsa-mblist.c:2115 +#, c-format +msgid "Shown mailbox: %s with %d message, " +msgid_plural "Shown mailbox: %s with %d messages, " +msgstr[0] "Hiển thị hộp thư: %s với %d thư, " +msgstr[1] "Hiển thị hộp thư: %s với %d thư, " + +#. xgettext: this is the second part of the message +#. * "Shown mailbox: %s with %d messages, %ld new". +#: ../src/balsa-mblist.c:2121 +#, c-format +msgid "%ld new" +msgid_plural "%ld new" +msgstr[0] "%ld mới" + +#: ../src/balsa-message.c:291 +msgid "Check cryptographic signature" +msgstr "Kiểm tra chữ ký mật mã" + +#: ../src/balsa-message.c:312 +msgid "Select message part to display" +msgstr "Chọn phần thư cần hiển thị" + +#: ../src/balsa-message.c:386 ../extensions/page-info/page-info-dialog.c:1783 +msgid "Content" +msgstr "Nội dung" + +#: ../src/balsa-message.c:464 +msgid "Message parts" +msgstr "Phần thư" + +#: ../src/balsa-message.c:645 +msgid "Save selected as..." +msgstr "Lưu điều chọn dạng..." + +#: ../src/balsa-message.c:652 +msgid "Save selected to folder..." +msgstr "Lưu điều chọn vào thư mục..." + +#: ../src/balsa-message.c:790 +msgid "(No sender)" +msgstr "(Không có người gởi)" + +#: ../src/balsa-message.c:858 +#, c-format +msgid "Could not access message %ld in mailbox \"%s\"." +msgstr "Không thể truy cập thư %ld trong hộp thư « %s »." + +#: ../src/balsa-message.c:1040 +msgid "mixed parts" +msgstr "pha phần" + +#: ../src/balsa-message.c:1042 +msgid "alternative parts" +msgstr "phần xen kẽ" + +#: ../src/balsa-message.c:1044 +msgid "signed parts" +msgstr "phần đã ký" + +#: ../src/balsa-message.c:1046 +msgid "encrypted parts" +msgstr "phần mật mã" + +#: ../src/balsa-message.c:1048 +msgid "rfc822 message" +msgstr "thư rfc822" + +#: ../src/balsa-message.c:1050 +#, c-format +msgid "\"%s\" parts" +msgstr "phần « %s »" + +#: ../src/balsa-message.c:1103 +#, c-format +msgid "rfc822 message (from %s, subject \"%s\")" +msgstr "thư rfc822 (từ %s, chủ đề « %s »)" + +#: ../src/balsa-message.c:1110 +msgid "complete message" +msgstr "toàn bộ thư" + +#: ../src/balsa-message.c:1127 +#, c-format +msgid "part %s: %s (file %s)" +msgstr "phần %s: %s (tập tin %s)" + +#: ../src/balsa-message.c:1138 +#, c-format +msgid "part %s: %s" +msgstr "phần %s: %s" + +#: ../src/balsa-message.c:1153 +msgid "encrypted: " +msgstr "mật mã: " + +#. #-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-# +#. If instead we dont have a path yet, fire up a file selector +#: ../src/balsa-message.c:1300 ../src/balsa-mime-widget-text.c:437 +#: ../src/balsa-mime-widget-text.c:822 gpe-go.c:1358 +#: ../data/playlist.glade.h:6 ../src/glade-project-window.c:466 +msgid "Save..." +msgstr "Lưu..." + +#: ../src/balsa-message.c:1376 +msgid "Select folder for saving selected parts" +msgstr "Chọn thư mục nơi cần lưu các phần đã chọn." + +#: ../src/balsa-message.c:1414 +#, c-format +msgid "%s message part" +msgstr "Phần thư %s" + +#: ../src/balsa-message.c:1442 ../src/balsa-mime-widget-callbacks.c:202 +#, c-format +msgid "Could not save %s: %s" +msgstr "Không thể lưu %s: %s" + +#: ../src/balsa-message.c:2140 +#, c-format +msgid "" +"The sender of this mail, %s, requested \n" +"a Message Disposition Notification(MDN) to be returned to `%s'.\n" +"Do you want to send this notification?" +msgstr "" +"Người gởi thư này, %s, yêu cầu \n" +"gởi trả lại Thông báo cách chuyển nhượng thư (MDN) đến « %s ».\n" +"Bạn có muốn gởi thông báo này không?" + +#: ../src/balsa-message.c:2147 +msgid "Reply to MDN?" +msgstr "Trả lời yêu cầu thông báo chuyển nhượng không?" + +#: ../src/balsa-message.c:2342 +msgid "" +"The decryption cannot be performed because this message is displayed more " +"than once.\n" +"Please close the other instances of this message and try again." +msgstr "" +"Không thể giải mật mã vì thư này được hiển thị nhiều lần.\n" +"Hãy đóng các thể hiện khác của thư này, rồi thử lại." + +#: ../src/balsa-message.c:2363 ../src/balsa-message.c:2445 +#: ../src/balsa-message.c:2560 +#, c-format +msgid "Parsing a message part failed: %s" +msgstr "Việc phân tách phần thư bị lỗi: %s" + +#: ../src/balsa-message.c:2364 ../src/balsa-message.c:2446 +#: ../src/balsa-message.c:2561 +msgid "Possible disk space problem." +msgstr "Có lẽ không có đủ chỗ trên đĩa." + +#: ../src/balsa-message.c:2376 +#, c-format +msgid "" +"The message sent by %s with subject \"%s\" contains an encrypted part, but " +"it's structure is invalid." +msgstr "" +"Thư được gởi bởi %s với chủ đề « %s » chứa một phần đã mật mã với cấu trúc " +"không hợp lệ." + +#: ../src/balsa-message.c:2384 +#, c-format +msgid "" +"The message sent by %s with subject \"%s\" contains a PGP encrypted part, " +"but this crypto protocol is not available." +msgstr "" +"Thư được gởi bởi %s với chủ đề « %s » chứa một phần đã mật mã PGP, nhưng " +"giao thức mật mã này chưa sẵn sàng." + +#: ../src/balsa-message.c:2397 +#, c-format +msgid "" +"The message sent by %s with subject \"%s\" contains a S/MIME encrypted part, " +"but this crypto protocol is not available." +msgstr "" +"Thư được gởi bởi %s với chủ đề « %s » chứa một phần đã mật mã S/MIME, nhưng " +"giao thức mật mã này chưa sẵn sàng." + +#: ../src/balsa-message.c:2461 +#, c-format +msgid "" +"The message sent by %s with subject \"%s\" contains a signed part, but its " +"structure is invalid. The signature, if there is any, cannot be checked." +msgstr "" +"Thư được gởi bởi %s với chủ đề « %s » chứa một phần đã ký tên với cấu trúc " +"không hợp lệ. Như thế thì không thể kiểm tra chữ ký, nếu có." + +#: ../src/balsa-message.c:2474 +#, c-format +msgid "" +"The message sent by %s with subject \"%s\" contains a %s signed part, but " +"this crypto protocol is not available." +msgstr "" +"Thư được gởi bởi %s với chủ đề « %s » chứa một phần đã ký tên %s, nhưng giao " +"thức mật mã này chưa sẵn sàng." + +# Name: don't translate / Tên: đừng dịch +#: ../src/balsa-message.c:2477 ../mimedir/mimedir-vcard.c:3662 +msgid "PGP" +msgstr "PGP" + +# Name: don't translate / Tên: đừng dịch +#: ../src/balsa-message.c:2477 ../src/balsa-mime-widget-crypto.c:72 +#: ../src/print.c:1302 +msgid "S/MIME" +msgstr "S/MIME" + +#: ../src/balsa-message.c:2496 ../src/balsa-message.c:2622 +msgid "Detected a good signature" +msgstr "Mới phát hiện một chữ ký đúng." + +#: ../src/balsa-message.c:2502 +msgid "Detected a good signature with insufficient validity" +msgstr "Mới phát hiện một chữ ký đúng không có đủ độ hợp lệ." + +#: ../src/balsa-message.c:2507 ../src/balsa-message.c:2626 +msgid "Detected a good signature with insufficient validity/trust" +msgstr "Mới phát hiện một chữ ký đúng không có đủ độ hợp lệ/tin cây." + +#: ../src/balsa-message.c:2514 ../src/balsa-message.c:2632 +#, c-format +msgid "" +"Checking the signature of the message sent by %s with subject \"%s\" " +"returned:\n" +"%s" +msgstr "" +"Việc kiểm tra chữ ký của thư được gởi bời %s với chủ đề « %s » đã gởi trả:\n" +"%s" + +#: ../src/balsa-message.c:2526 +#, c-format +msgid "" +"Checking the signature of the message sent by %s with subject \"%s\" failed " +"with an error!" +msgstr "" +"Việc kiểm tra chữ ký của thư được gởi bời %s với chủ đề « %s » đã thất bại " +"với lỗi !" + +#: ../src/balsa-message.c:2584 +msgid "" +"The decryption cannot be performed because this message is displayed more " +"than once." +msgstr "Không thể giải mật mã vì thư này được hiển thị nhiều lần." + +#: ../src/balsa-message.c:2586 +msgid "" +"The signature check and removal of the OpenPGP armor cannot be performed " +"because this message is displayed more than once." +msgstr "" +"Không thể kiểm tra chữ ký và gỡ bỏ vỏ bọc kim thuộc OpenPGP vì thư này được " +"hiển thị nhiều lần." + +#: ../src/balsa-message.c:2589 +msgid "Please close the other instances of this message and try again." +msgstr "Hãy đóng các thể hiện khác của thư này, rồi thử lại." + +#: ../src/filter-edit-callbacks.c:311 +msgid "One of the specified fields contains:" +msgstr "Một của những trường đã xác định chứa:" + +#: ../src/filter-edit-callbacks.c:312 +msgid "None of the specified fields contains:" +msgstr "Không có trường đã xác định chứa:" + +#: ../src/filter-edit-callbacks.c:314 +msgid "One of the regular expressions matches" +msgstr "Một của những biểu thức chính quy khớp" + +#: ../src/filter-edit-callbacks.c:315 +msgid "None of the regular expressions matches" +msgstr "Không có biểu thức chính quy khớp" + +#: ../src/filter-edit-callbacks.c:317 +msgid "Match when date is in the interval:" +msgstr "Khớp khi ngày nằm trong khoảng:" + +#: ../src/filter-edit-callbacks.c:318 +msgid "Match when date is outside the interval:" +msgstr "Khớp khi ngày nằm ngoài khoảng:" + +#: ../src/filter-edit-callbacks.c:320 ../src/filter-edit-callbacks.c:1085 +msgid "Match when one of these flags is set:" +msgstr "Khớp khi đặt một trong những cờ hiệu này:" + +#: ../src/filter-edit-callbacks.c:321 +msgid "Match when none of these flags is set:" +msgstr "Khớp khi không đặt các cờ hiệu này:" + +#: ../src/filter-edit-callbacks.c:512 +msgid "You must specify the name of the user header to match on" +msgstr "Bạn cần chỉ định tên của dòng đầu người dùng cần khớp theo nó" + +#: ../src/filter-edit-callbacks.c:523 +msgid "You must specify at least one field for matching" +msgstr "Phải ghi rõ ít nhất một trường cần khớp" + +#: ../src/filter-edit-callbacks.c:533 +msgid "You must provide a string" +msgstr "Phải ghi rõ một chuỗi" + +#: ../src/filter-edit-callbacks.c:540 +msgid "You must provide at least one regular expression" +msgstr "Phải ghi rõ ít nhất một biểu thức chính quy" + +#: ../src/filter-edit-callbacks.c:552 +msgid "Low date is incorrect" +msgstr "Ngày dưới không đúng" + +#: ../src/filter-edit-callbacks.c:563 +msgid "High date is incorrect" +msgstr "Ngày trên không đúng" + +#: ../src/filter-edit-callbacks.c:571 +msgid "Low date is greater than high date" +msgstr "Ngày dưới ở trên ngày trên" + +#: ../src/filter-edit-callbacks.c:856 +#, c-format +msgid "Error displaying condition help: %s\n" +msgstr "Gặp lỗi khi hiển thị trợ giúp về điều kiện: %s\n" + +#: ../src/filter-edit-callbacks.c:897 +msgid "Match Fields" +msgstr "Khớp trường" + +#: ../src/filter-edit-callbacks.c:907 ../app/actions/select-actions.c:50 +#: src/gtkam-main.c:553 src/gtkam-main.c:555 src/gtkam-main.c:562 +msgid "_All" +msgstr "_Tất cả" + +#: ../libgnomeui/gnome-app-helper.c:171 ../ui/history.glade.h:2 +msgid "C_lear" +msgstr "_Xoá" + +#: ../src/filter-edit-callbacks.c:910 ../src/main-window.c:3977 +msgid "_Body" +msgstr "Th_ân" + +#: ../src/sendmsg-window.c:2818 ../composer/e-msg-composer-hdrs.c:646 +msgid "_To:" +msgstr "Ch_o:" + +#: ../src/filter-edit-callbacks.c:912 ../src/main-window.c:3979 +#: ../embed/print-dialog.c:392 ../src/drivel.glade.h:69 +msgid "_From:" +msgstr "_Từ :" + +#: ../src/filter-edit-callbacks.c:913 +msgid "_Subject" +msgstr "C_hủ đề" + +#: ../src/filter-edit-callbacks.c:914 ../src/main-window.c:3981 +#: ../composer/e-msg-composer-hdrs.c:650 ../composer/e-msg-composer-hdrs.c:652 +msgid "_Cc:" +msgstr "_Cc:" + +#: ../src/filter-edit-callbacks.c:916 +msgid "_User header:" +msgstr "Dòng đầu _người dùng:" + +#: ../pan/grouplist.c:992 ../pan/rules/rule-edit-ui.c:153 +msgid "Unread" +msgstr "Chưa đọc" + +#: ../src/filter-edit-callbacks.c:946 ../mail/em-filter-i18n.h:12 +msgid "Deleted" +msgstr "Đã xoá bỏ" + +#: ../src/filter-edit-callbacks.c:946 +msgid "Replied" +msgstr "Đã trả lời" + +#: ../src/filter-edit-callbacks.c:946 ../mail/message-list.etspec.h:5 +msgid "Flagged" +msgstr "Đã đặt cờ" + +#: ../src/filter-edit-callbacks.c:961 +msgid "One of the specified f_ields contains" +msgstr "Một của những trường đã xác định chứa" + +#: ../src/filter-edit-callbacks.c:976 +msgid "Contain/Does _Not Contain" +msgstr "Chứa/_Không chứa" + +#: ../src/filter-edit-callbacks.c:995 +msgid "_One of the regular expressions matches" +msgstr "Một của những _biểu thức chính quy khớp" + +#: ../plug-ins/gimpressionist/sizemap.c:472 +msgid "A_dd" +msgstr "Th_êm" + +#: ../src/user_popup.c:605 ../src/bitmapfamily-win.cc:90 +#: ../src/guikachu.glade.h:20 ../src/mainwin-menu.cc:104 +msgid "_Remove" +msgstr "_Gỡ bỏ" + +#: ../src/filter-edit-callbacks.c:1030 +msgid "One _Matches/None Matches" +msgstr "Một cái khớ_p/Không có cái nào khớp" + +#: ../src/filter-edit-callbacks.c:1047 +msgid "Match when message date is in the interval:" +msgstr "Khớp khi ngày thư nằm trong khoảng:" + +#: ../src/filter-edit-callbacks.c:1067 +msgid "Inside/outside the date interval" +msgstr "Trong/Ngoài khoảng ngày" + +#: ../src/filter-edit-callbacks.c:1114 +msgid "Match when one flag is set/when no flag is set" +msgstr "Khớp khi một cờ hiệu được đặt/Khi không có cờ hiệu được đặt" + +#: ../src/filter-edit-callbacks.c:1132 +msgid "Search T_ype:" +msgstr "_Kiểu tìm kiếm:" + +#: ../src/filter-edit-callbacks.c:1206 +msgid "Edit condition for filter: " +msgstr "Sửa điều kiện lọc: " + +#: ../src/filter-edit-callbacks.c:1424 ../src/save-restore.c:1845 +msgid "Filter with no condition was omitted" +msgstr "Lọc không có điều kiện thì bị bỏ sót" + +#: ../src/filter-edit-callbacks.c:1450 +#, c-format +msgid "Error displaying filter help: %s\n" +msgstr "Gặp lỗi khi hiển thị trợ giúp về bộ lọc: %s\n" + +#: ../src/filter-edit-callbacks.c:1640 +msgid "New filter" +msgstr "Bộ lọc mới" + +#: ../src/filter-edit-callbacks.c:1772 +msgid "No filter name specified." +msgstr "Chưa ghi rõ tên bộ lọc khác." + +#: ../src/filter-edit-callbacks.c:1777 +#, c-format +msgid "Filter \"%s\" already exists." +msgstr "Bộ lọc « %s » đã có." + +#: ../src/filter-edit-callbacks.c:1787 +msgid "Filter must have conditions." +msgstr "Bộ lọc phải có điều kiện." + +#: ../src/filter-edit-callbacks.c:1844 +msgid "Filter has matched" +msgstr "Bộ lọc đã khớp" + +#: ../src/filter-edit-callbacks.c:1869 +msgid "You must provide a sound to play" +msgstr "Phải cung cấp âm thanh cần phát" + +#: ../src/filter-edit-callbacks.c:2028 +#, c-format +msgid "(Example: write December 31, 2000, as %s)" +msgstr "(Ví dụ : viết Ngày 31, tháng Chạp, năm 2000, dạng %s)" + +#: ../src/filter-edit-dialog.c:89 ../src/pref-manager.c:384 +#: ../plug-ins/ifscompose/ifscompose.c:642 ../objects/UML/message.c:138 +#: ../src/form-editor/button-prop.cc:145 +msgid "Simple" +msgstr "Đơn giản" + +#: ../src/filter-edit-dialog.c:90 +#: ../gtksourceview/language-specs/ruby.lang.h:14 +msgid "Regular Expression" +msgstr "Biểu thức chính quy" + +#: ../src/filter-edit-dialog.c:91 +msgid "Date interval" +msgstr "Khoảng ngày" + +#: ../src/filter-edit-dialog.c:92 +msgid "Flag condition" +msgstr "Điều kiện cờ" + +#: ../src/filter-edit-dialog.c:96 +msgid "Copy to folder:" +msgstr "Chép vào thư mục:" + +#: ../src/filter-edit-dialog.c:97 +msgid "Move to folder:" +msgstr "Chuyển vào thư mục:" + +#: ../src/filter-edit-dialog.c:98 +msgid "Print on printer:" +msgstr "In bằng máy in:" + +#: ../src/filter-edit-dialog.c:99 +msgid "Run program:" +msgstr "Chạy chương trình:" + +#: ../src/filter-edit-dialog.c:100 +msgid "Send to Trash" +msgstr "Chuyển vào Rác" + +#: ../src/filter-edit-dialog.c:104 ../objects/GRAFCET/vergent.c:122 +msgid "OR" +msgstr "HOẶC" + +#: ../src/filter-edit-dialog.c:105 ../objects/GRAFCET/vergent.c:123 +msgid "AND" +msgstr "VÀ" + +#: ../gmedia_slice/interface.c:459 +msgid "_New" +msgstr "_Mới" + +#. The name entry +#: ../src/filter-edit-dialog.c:249 +msgid "_Filter name:" +msgstr "Tên bộ _lọc:" + +#. The filter op-code : "OR" or "AND" all the conditions +#: ../src/filter-edit-dialog.c:267 +msgid "Operation between conditions" +msgstr "Thao tác giữa các điều kiện" + +#: ../Pyblio/GnomeUI/Document.py:145 ../Pyblio/GnomeUI/Document.py:194 +#: ../storage/sunone-permissions-dialog.glade.h:30 po/silky.glade.h:214 +#: app/menubar.c:687 +msgid "_Edit" +msgstr "_Hiệu chỉnh" + +#: ../src/filter-edit-dialog.c:317 +msgid "Ne_w" +msgstr "_Mới" + +#. The notification area +#: ../src/filter-edit-dialog.c:350 +msgid "Notification:" +msgstr "Thông báo :" + +#. Notification buttons +#: ../src/filter-edit-dialog.c:360 +msgid "Play sound:" +msgstr "Phát âm:" + +#: ../src/filter-edit-dialog.c:367 ../src/filter-edit-dialog.c:380 +msgid "Use Sound..." +msgstr "Dùng âm thanh..." + +#: ../src/filter-edit-dialog.c:399 +msgid "Popup text:" +msgstr "Chuỗi bật lên:" + +#. The action area +#: ../src/filter-edit-dialog.c:420 +msgid "Action to perform:" +msgstr "Hành động cần thực hiện:" + +#: ../data/glade/smart-playlist-dialog.glade.h:5 +msgid "Match" +msgstr "Khớp" + +#: ../testing/gda-test-sql.c:289 ../src/logout.c:224 libexif/exif-entry.c:479 +#: ../mimedir/mimedir-vcomponent.c:407 ../mimedir/mimedir-vcomponent.c:408 +msgid "Action" +msgstr "Hành động" + +#: ../src/filter-edit-dialog.c:486 ../app/widgets/gimpdataeditor.c:229 +#: ../app/sheets_dialog.c:267 ../glom/glom.glade.h:135 +msgid "Revert" +msgstr "Hoàn nguyên" + +#: ../src/filter-edit-dialog.c:537 +msgid "A filter run dialog is open.Close it before you can modify filters." +msgstr "" +"Có một hộp thoại chạy bộ lọc được mở. Hãy đóng nó trước khi sửa đổi bộ lọc." + +#: ../src/filter-edit-dialog.c:550 +msgid "Balsa Filters" +msgstr "Bộ lọc Balsa" + +#: ../src/filter-edit-dialog.c:618 +#, c-format +msgid "Filter \"%s\" has no condition." +msgstr "Bộ lọc « %s » không có điều kiện." + +#: ../src/filter-export-callbacks.c:57 +#, c-format +msgid "Unable to export filter %s, an error occurred." +msgstr "Không thể xuất ra bộ lọc %s vì gặp lỗi." + +#: ../src/filter-export-dialog.c:63 +msgid "" +"There are opened filter run dialogs, close them before you can modify " +"filters." +msgstr "" +"Có một số hộp thoại chạy bộ lọc được mở. Hãy đóng nó trước khi sửa đổi trình " +"lọc." + +#: ../src/filter-export-dialog.c:75 +msgid "Balsa Filters Export" +msgstr "Xuất bộ lọc Balsa" + +#: ../src/filter-run-callbacks.c:182 +#, c-format +msgid "Error displaying run filters help: %s\n" +msgstr "Gặp lỗi khi hiển thị trợ giúp về chạy bộ lọc: %s\n" + +#: ../src/filter-run-callbacks.c:230 ../src/filter-run-callbacks.c:252 +msgid "Error when applying filters" +msgstr "Gặp lỗi khi áp dụng bộ lọc" + +#: ../src/filter-run-callbacks.c:233 ../src/filter-run-callbacks.c:255 +#, c-format +msgid "Filter applied to \"%s\"." +msgstr "Bộ lọc được áp dụng cho « %s »." + +#: ../src/filter-run-callbacks.c:292 +#, c-format +msgid "" +"The destination mailbox of the filter \"%s\" is \"%s\".\n" +"You can't associate it with the same mailbox (that causes recursion)." +msgstr "" +"Hộp thư đích của bộ lọc « %s » là « %s ».\n" +"Không thể gắn nó liên quan với cùng hộp thư (sẽ gây đệ qui)." + +#: ../src/filter-run-callbacks.c:300 +#, c-format +msgid "" +"The filter \"%s\" is not compatible with the mailbox type of \"%s\".\n" +"This happens for example when you use regular expressions match with IMAP " +"mailboxes, it is done by a very slow method; if possible, use substring " +"match instead." +msgstr "" +"Bộ lọc « %s » không tương thích với kiểu hộp thư « %s ».\n" +"Trường hợp này xảy ra, lấy thí dụ, khi bạn sử dụng cách khớp biểu thức chính " +"quy với hộp thư kiểu IMAP: phương pháp rất chậm. Nếu có thể, hãy sử dụng " +"cách khớp chuỗi phụ thay thế." + +#: ../src/filter-run-dialog.c:189 +msgid "Balsa Filters of Mailbox: " +msgstr "Bộ lọc Balsa của hộp thư : " + +#: ../src/filter-run-dialog.c:230 +msgid "On reception" +msgstr "Khi nhận" + +#: ../src/filter-run-dialog.c:242 +msgid "On exit" +msgstr "Khi thoát" + +#: ../src/filter-run-dialog.c:316 +msgid "Apply Selected" +msgstr "Áp dụng điều chọn" + +#: ui/galeon-ui.xml.in.h:203 ../glade2/meldapp.glade.h:98 +msgid "_Up" +msgstr "_Lên" + +#. down button +#: ../src/filter-run-dialog.c:369 +msgid "Do_wn" +msgstr "_Xuống" + +#: ../src/filter-run-dialog.c:375 +msgid "A_pply Now!" +msgstr "Á_p dụng ngay!" + +#: ../src/filter-run-dialog.c:419 +msgid "" +"The filters dialog is opened, close it before you can run filters on any " +"mailbox" +msgstr "" +"Đã mở hộp thoại của bộ lọc, hãy đóng lại trước khi chạy bộ lọc cho bất kỳ " +"hộp thư nào." + +#: ../src/folder-conf.c:281 +msgid "Remote IMAP folder" +msgstr "Thư mục IMAP ở xa" + +#: ../plug-ins/imagemap/imap_polygon.c:515 ../src/menus.c:68 +msgid "_Update" +msgstr "_Cập nhật" + +#: ../src/folder-conf.c:284 ../sources/rb-playlist-source-recorder.c:1114 +#: ../gtk/gtkfilesel.c:1522 +msgid "C_reate" +msgstr "_Tạo" + +#: ../src/mailbox-conf.c:1232 ../gcalctool/gtk.c:350 +msgid "_Basic" +msgstr "Cơ _bản" + +#: ../src/folder-conf.c:312 +msgid "_Max number of connections:" +msgstr "Số kết nối tối _đa:" + +#: ../src/folder-conf.c:319 ../src/mailbox-conf.c:1295 +msgid "Enable _persistent cache" +msgstr "Bật bộ nhớ tạm _bền bỉ" + +#: ../src/folder-conf.c:327 ../src/mailbox-conf.c:1299 +msgid "Enable _bug workarounds" +msgstr "Bật cách chỉnh sửa _lỗi" + +#: ../src/folder-conf.c:334 +msgid "Use STATUS for mailbox checking" +msgstr "Dùng TRẠNG THÁI để kiểm tra hộp thư" + +#: ../plug-ins/common/CML_explorer.c:1307 +msgid "_Advanced" +msgstr "Cấp c_ao" + +#: ../src/folder-conf.c:343 +msgid "Descriptive _Name:" +msgstr "T_ên mô tả:" + +#: ../src/folder-conf.c:358 ../src/mailbox-conf.c:1146 +msgid "Use_r name:" +msgstr "Tên _người dùng:" + +#: ../glade/straw.glade.h:87 +msgid "_Password:" +msgstr "_Mật khẩu :" + +#: ../src/folder-conf.c:369 ../src/mailbox-conf.c:1257 +msgid "_Anonymous access" +msgstr "Truy cấp _vô danh" + +#: ../src/folder-conf.c:378 +msgid "Subscribed _folders only" +msgstr "Chỉ thư mục đã _đăng ký" + +#: ../src/folder-conf.c:380 +msgid "Always show _INBOX" +msgstr "Luôn hiện THƯ _ĐẾN" + +#: ../src/folder-conf.c:383 +msgid "Pr_efix:" +msgstr "T_iền tố :" + +#: ../src/folder-conf.c:529 +msgid "Select parent folder" +msgstr "Chọn thư mục mẹ" + +#: ../src/folder-conf.c:603 +#, c-format +msgid "" +"Renaming INBOX is special!\n" +"You will create a subfolder %s in %s\n" +"containing the messages from INBOX.\n" +"INBOX and its subfolders will remain.\n" +"What would you like to do?" +msgstr "" +"Đổi tên hộp THƯ ĐẾN là đặc biệt!\n" +"Bạn sẽ tạo thư mục con %s trong %s\n" +"chứa các thư từ hộp THƯ ĐẾN.\n" +"Vẫn duy trì hộp THƯ ĐẾN và các thư\n" +"mục con của nó. Muốn thực hiện không?" + +#: ../src/folder-conf.c:609 ../src/StockIcons.cs:33 ../gtk/gtkstock.c:311 +#: ../widgets/gtk+.xml.in.h:149 app/gui-subs.c:483 app/gui-subs.c:554 +msgid "Question" +msgstr "Câu hỏi" + +#: ../src/folder-conf.c:614 +msgid "Rename INBOX" +msgstr "Đổi tên hộp THƯ ĐẾN" + +#: ../src/folder-conf.c:616 web/template/keywords_transl_main.tpl:15 +#: ../app/dialogs.c:52 ../app/paginate_psprint.c:314 ../glade/property.c:5150 +#: ../src/dirbrowser.c:288 ../src/filexferdlg.c:254 +#: ../src/mlview-attribute-picker.cc:165 ../widgets/gtk+.xml.in.h:29 +#: address_gui.c:2700 category.c:421 category.c:877 category.c:917 +#: datebook_gui.c:1154 datebook_gui.c:1582 datebook_gui.c:4371 +#: export_gui.c:344 memo_gui.c:1552 password.c:362 print_gui.c:338 +#: restore_gui.c:312 todo_gui.c:2169 utils.c:1094 KeyRing/keyring.c:1332 +#: app/gui-subs.c:507 app/gui-subs.c:571 app/keys.c:711 +#: app/midi-settings-050.c:637 app/midi-settings-09x.c:640 +#: app/sample-editor.c:1486 app/sample-editor.c:1661 app/sample-editor.c:1985 +msgid "Cancel" +msgstr "Thôi" + +#: ../src/folder-conf.c:637 +#, c-format +msgid "Folder rename failed. Reason: %s" +msgstr "Việc thay đổi tên thư mục bị lỗi. Lý do : %s" + +#: ../src/folder-conf.c:689 +#, c-format +msgid "Folder creation failed. Reason: %s" +msgstr "Việc tạo thư mục bị lỗi. Lý do : %s" + +#: ../src/folder-conf.c:729 +msgid "" +"An IMAP folder that is not a mailbox\n" +"has no properties that can be changed." +msgstr "" +"Một thư mục IMAP không phải là hộp thư\n" +"không có thuộc tính để có thể thay đổi." + +#: ../src/folder-conf.c:744 +msgid "Remote IMAP subfolder" +msgstr "Thư mục con IMAP ở xa" + +#: ../src/folder-conf.c:747 ../profiles/gnome-audio-profiles.glade2.h:9 +msgid "_Create" +msgstr "_Tạo" + +#: ../src/folder-conf.c:768 +msgid "Rename or move subfolder" +msgstr "Đổi tên hay di chuyển thư mục con" + +#: ../src/folder-conf.c:769 +msgid "Create subfolder" +msgstr "Tạo thư mục con" + +#: ../src/folder-conf.c:777 ../gtk/gtkfilesel.c:1498 +msgid "_Folder name:" +msgstr "Tên thư _mục:" + +#: ../src/folder-conf.c:783 ../ui/connect.glade.h:7 +msgid "Host:" +msgstr "Máy:" + +#: ../src/folder-conf.c:789 ../plug-ins/script-fu/script-fu-console.c:256 +#: src/fe-gtk/setup.c:1382 ../pan/pan-file-entry.c:81 +msgid "_Browse..." +msgstr "_Duyệt..." + +#: ../src/folder-conf.c:797 +msgid "_Subfolder of:" +msgstr "Thư mục _con của:" + +#: ../src/folder-conf.c:826 +msgid "" +"This folder is not stored in configuration. I do not yet know how to remove " +"it from remote server." +msgstr "" +"Thư mục này không được lưu trữ trong cấu hình, nên còn chưa biết cách gỡ bỏ " +"nó khỏi máy phục vụ ở xa." + +#: ../src/folder-conf.c:835 +#, c-format +msgid "" +"This will remove the folder \"%s\" from the list.\n" +"You may use \"New IMAP Folder\" later to add this folder again.\n" +msgstr "" +"Việc này sẽ gỡ bỏ thư mục « %s » ra khỏi danh sách.\n" +"Có thể dùng tính năng « Thư mục IMAP mới » sau này để bổ sung lại thư mục " +"này.\n" + +#: ../src/folder-conf.c:840 ../main.c:84 ../main.c:196 ../structure.c:173 +#: list-ui.c:81 ../libgimp/gimpexport.c:387 fileops.c:429 +msgid "Confirm" +msgstr "Xác nhận" + +#: ../src/information-dialog.c:222 +msgid "Information - Balsa" +msgstr "Thông tin — Balsa" + +#: ../src/information-dialog.c:325 ../libgnomedb/gnome-db-sql-console.c:561 +#, c-format +msgid "WARNING: " +msgstr "CẢNH BÁO :" + +#: ../src/information-dialog.c:328 ../libgnomedb/gnome-db-sql-console.c:566 +#, c-format +msgid "ERROR: " +msgstr "LỖI :" + +#: ../src/information-dialog.c:331 +#, c-format +msgid "FATAL: " +msgstr "NGHIÊM TRỌNG: " + +#: ../src/mailbox-conf.c:215 +msgid "Use _SSL" +msgstr "Dùng _SSL" + +#: ../src/mailbox-conf.c:342 +msgid "No mailbox selected." +msgstr "Chưa chọn hộp thư." + +#: ../src/mailbox-conf.c:371 +#, c-format +msgid "" +"Mailbox \"%s\" is used by Balsa and I cannot remove it.\n" +"If you really want to remove it, assign its function\n" +"to some other mailbox." +msgstr "" +"Balsa dùng hộp thư « %s » nên không thể gỡ bỏ nó.\n" +"Nếu bạn thật sự muốn gỡ bỏ nó, hãy cấp phát chức \n" +"năng của nó cho hộp thư khác nào đó." + +#: ../src/mailbox-conf.c:381 +#, c-format +msgid "" +"This will remove the mailbox \"%s\" from the list of mailboxes. You may " +"also delete the disk file or files associated with this mailbox.\n" +"If you do not remove the file on disk you may \"Add Mailbox\" to access the " +"mailbox again.\n" +"What would you like to do?" +msgstr "" +"Việc này sẽ gỡ bỏ hộp thư « %s » khỏi danh sách các hộp thư.\n" +"Bạn cũng có thể xoá bỏ tập tin trên đĩa hay tập tin liên quan với hộp thư " +"này.\n" +"Nếu không xoá b6o tập tin của nó trên đĩa thì có thể « Thêm hộp thư » để " +"truy cập lại nó sau này.\n" +"Bạn thích làm gì ?" + +#: ../src/mailbox-conf.c:393 +msgid "Remove from _list" +msgstr "Gỡ bỏ ra _danh sách" + +#: ../src/mailbox-conf.c:394 +msgid "Remove from list and _disk" +msgstr "Gỡ bỏ ra danh sách và _đĩa" + +#: ../src/mailbox-conf.c:402 +#, c-format +msgid "" +"This will remove the mailbox \"%s\" and all its messages from your IMAP " +"server. If %s has subfolders, it will still appear as a node in the folder " +"tree.\n" +"You may use \"New IMAP subfolder\" later to add a mailbox with this name.\n" +"What would you like to do?" +msgstr "" +"Việc này sẽ gỡ bỏ hộp thư « %s » và mọi thư của nó ra khỏi máy phục vụ IMAP. " +"Nếu %s có thư mục con, nó vẫn sẽ xuất hiện là một nút trong cây thư mục.\n" +"Có thể dùng tính năng « Thư mục con IMAP mới » sau này để thêm một hộp thư " +"với tên này.\n" +"Bạn thích làm gì?" + +#: ../src/mailbox-conf.c:415 +msgid "_Remove from server" +msgstr "Gỡ _bỏ ra máy phục vụ" + +#: ../src/mailbox-conf.c:422 +#, c-format +msgid "" +"This will remove the mailbox \"%s\" from the list of mailboxes.\n" +"You may use \"Add Mailbox\" later to access this mailbox again.\n" +"What would you like to do?" +msgstr "" +"Việc này sẽ gỡ bỏ hộp thư « %s » khỏi danh sách hộp thư\n" +"Có thể dùng tính năng « Thêm Hộp Thư » về sau để truy cập lại hộp thư này.\n" +"Bạn thích làm gì?" + +#: ../src/mailbox-conf.c:431 +msgid "_Remove from list" +msgstr "Gỡ _bỏ ra danh sách" + +#: ../src/mailbox-conf.c:469 +#, c-format +msgid "Folder deletion failed. Reason: %s" +msgstr "Việc xoá bỏ thư mục bị lỗi. Lý do : %s" + +#: ../src/mailbox-conf.c:526 ../src/pref-manager.c:1511 +#: ../src/pref-manager.c:1596 ../src/pref-manager.c:2645 +#: ../gtk/gtkfilechooserdefault.c:3327 ../gtk/gtkstock.c:317 +#: ../glade/glade_menu_editor.c:958 +msgid "_Add" +msgstr "Th_êm" + +#: ../src/mailbox-conf.c:746 +#: ../libnautilus-private/nautilus-file-operations.c:361 +#: ../libnautilus-private/nautilus-file-operations.c:386 +#: ../libnautilus-private/nautilus-file-utilities.c:70 +#: ../src/nautilus-connect-server-dialog.c:287 +#: ../src/baobab-remote-connect-dialog.c:291 +#, c-format +msgid "%s on %s" +msgstr "%s trên %s" + +#: ../src/mailbox-conf.c:888 +#, c-format +msgid "" +"Rename of %s to %s failed:\n" +"%s" +msgstr "" +"Việc thay đổi tên %s thành %s bị lỗi:\n" +"%s" + +#: ../src/mailbox-conf.c:1067 +msgid "_Mailbox Name:" +msgstr "Tên _hộp thư :" + +#: ../src/mailbox-conf.c:1078 +msgid "Local Mailbox Configurator" +msgstr "Bộ cấu hình thư cục bộ" + +#: ../src/mailbox-conf.c:1111 +msgid "Remote Mailbox Configurator" +msgstr "Bộ cấu hình thư ở xa" + +#. mailbox name +#: ../src/mailbox-conf.c:1133 ../src/mailbox-conf.c:1235 +msgid "Mailbox _Name:" +msgstr "T_ên hộp thư :" + +#: ../desktop/client/src/connect.c:578 +msgid "Pass_word:" +msgstr "_Mật khẩu :" + +#: ../src/mailbox-conf.c:1160 +msgid "_Delete messages from server after download" +msgstr "_Xoá bỏ thư ra máy phục vụ sau khi tải về" + +#: ../src/mailbox-conf.c:1165 +msgid "_Enable check for new mail" +msgstr "_Bật kiểm tra tìm thư mới" + +#: ../src/mailbox-conf.c:1170 +msgid "_Filter messages through procmail" +msgstr "_Lọc thư qua procmail" + +#: ../src/mailbox-conf.c:1174 +msgid "Fi_lter Command:" +msgstr "L_ọc lệnh:" + +#: ../src/mailbox-conf.c:1188 +msgid "Disable _APOP" +msgstr "Tắt _APOP" + +#: ../pan/server-ui.c:358 ../glade/straw.glade.h:95 +msgid "_Username:" +msgstr "T_ên người dùng:" + +#: ../src/mailbox-conf.c:1263 +msgid "_Remember Password" +msgstr "_Nhớ mật khẩu" + +#: ../src/mailbox-conf.c:1274 +msgid "F_older Path:" +msgstr "Đường dẫn thư _mục:" + +#: ../src/mailbox-conf.c:1360 +msgid "_Identity:" +msgstr "T_hực thể:" + +#: ../mail/em-account-editor.c:304 ../mail/em-account-editor.c:767 +#: ../widgets/gtk+.xml.in.h:10 +#, fuzzy +msgid "Always" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Luôn luôn\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Luôn" + +#: ../src/mailbox-conf.c:1392 +msgid "" +"_Decrypt and check\n" +"signatures automatically:" +msgstr "" +"Tự động giải mật mã\n" +"và kiểm tra chữ ký:" + +#: ../src/mailbox-conf.c:1413 +msgid "Show _Recipient column instead of Sender" +msgstr "Hiển thị cột _Người nhận thay thế Người gởi" + +#: ../src/mailbox-conf.c:1420 +msgid "_Subscribe for new mail check" +msgstr "Đăng _ký để kiểm tra tìm thư với" + +#: ../src/mailbox-node.c:265 +msgid "The folder edition to be written." +msgstr "Bản sao thư mục cần ghi." + +#: ../src/mailbox-node.c:433 +#, c-format +msgid "" +"Scanning of %s failed: %s\n" +"Check network connectivity." +msgstr "" +"Việc quét %s bị lỗi: %s\n" +"Kiểm tra xem đã kết nối đến mạng." + +#: ../src/mailbox-node.c:435 +#, c-format +msgid "Scanning of %s failed: %s" +msgstr "Việc quét %s bị lỗi: %s" + +#: ../src/mailbox-node.c:484 +#, c-format +msgid "Scanning %s. Please wait..." +msgstr "Đang quét %s. Hãy đời một chút..." + +#: ../src/mailbox-node.c:943 +msgid "Local _mbox mailbox..." +msgstr "Hộp thư _mbox cục bộ..." + +#: ../src/mailbox-node.c:945 +msgid "Local Mail_dir mailbox..." +msgstr "Hộp thư Mail_dir cục bộ..." + +#: ../src/mailbox-node.c:947 +msgid "Local M_H mailbox..." +msgstr "Hộp thư M_H cục bộ..." + +#: ../src/mailbox-node.c:949 +msgid "Remote _IMAP mailbox..." +msgstr "Hộp thư _IMAP ở xa..." + +#: ../src/mailbox-node.c:952 +msgid "Remote IMAP _folder..." +msgstr "Thư _mục IMAP ở xa..." + +#: ../src/mailbox-node.c:954 +msgid "Remote IMAP _subfolder..." +msgstr "Thư mục _con IMAP ở xa..." + +#: ../src/mailbox-node.c:965 ../src/mailbox-node.c:981 src/gtkam-main.c:567 +msgid "_Rescan" +msgstr "_Quét lại" + +#: ../src/mailbox-node.c:975 ../ui/muds.glade.h:54 +msgid "_Properties..." +msgstr "Th_uộc tính..." + +#: ../src/mailbox-node.c:991 ../extensions/page-info/page-info-dialog.c:1038 +msgid "_Open" +msgstr "_Mở" + +#: ../src/mailbox-node.c:1005 ../mail/mail-dialogs.glade.h:22 +#: ../extensions/rss/rss-ui.glade.h:3 ../glade/straw.glade.h:93 +msgid "_Subscribe" +msgstr "Đăng _ký" + +#: ../src/mailbox-node.c:1007 ../mail/mail-dialogs.glade.h:24 +#: ../plugins/folder-unsubscribe/org-gnome-mail-folder-unsubscribe.eplug.xml.h:3 +msgid "_Unsubscribe" +msgstr "_Bỏ đăng ký" + +#: ../src/mailbox-node.c:1013 +msgid "Mark as _Inbox" +msgstr "Đánh dấu hộp Thư _Đến" + +#: ../src/mailbox-node.c:1015 +msgid "_Mark as Sentbox" +msgstr "Đánh dấu hộp Đã _Gởi" + +#: ../src/mailbox-node.c:1017 +msgid "Mark as _Trash" +msgstr "Đánh dấu _Rác" + +#: ../src/mailbox-node.c:1019 +msgid "Mark as D_raftbox" +msgstr "Đánh dấu hộp _Nháp" + +#: ../src/mailbox-node.c:1022 +msgid "_Empty trash" +msgstr "Đổ Rá_c" + +#: ../src/mailbox-node.c:1027 +msgid "_Edit/Apply filters" +msgstr "_Sửa/Áp dụng bộ lọc" + +#: ../src/mailbox-node.c:1107 +#, c-format +msgid "The path \"%s\" does not lead to a mailbox." +msgstr "Đường dẫn « %s » không đi tới hộp thư." + +#: ../src/mailbox-node.c:1120 +#, c-format +msgid "Local mailbox %s loaded as: %s\n" +msgstr "Hộp thư cục bộ %s được tải dạng: %s\n" + +#: ../src/mailbox-node.c:1147 +#, c-format +msgid "Local folder %s\n" +msgstr "Thư mục cục bộ %s\n" + +#: ../src/main-window.c:280 +msgid "Balsa closes files and connections. Please wait..." +msgstr "Balsa đang đóng một số tập tin và kết nối. Hãy đợi một chút..." + +#: ../src/main-window.c:319 ../src/main-window.c:365 +msgid "_Deleted" +msgstr "Đã _xoá bỏ" + +#: ../src/main-window.c:322 +msgid "Un_Deleted" +msgstr "Ch_ưa xoá bỏ" + +#: ../src/main-window.c:325 ../src/file-manager/fm-properties-window.c:2802 +#: ../gmedia_slice/interface.c:424 +msgid "_Read" +msgstr "_Đã đọc" + +#: ../src/main-window.c:328 +msgid "Un_read" +msgstr "C_hưa đọc" + +#: ../src/main-window.c:334 +msgid "Un_flagged" +msgstr "Chư_a đặt cờ" + +#: ../src/main-window.c:337 ../src/main-window.c:377 +msgid "_Answered" +msgstr "Đã t_rả lời" + +#: ../src/main-window.c:340 +msgid "Un_answered" +msgstr "Chưa tr_ả lời" + +#: ../src/main-window.c:346 +msgid "_No Headers" +msgstr "Ẩ_n dòng đầu" + +#: ../src/main-window.c:346 +msgid "Display no headers" +msgstr "Không hiển thị dòng đầu" + +#: ../src/main-window.c:348 +msgid "S_elected Headers" +msgstr "Dòng đầu đã _chọn" + +#: ../src/main-window.c:349 +msgid "Display selected headers" +msgstr "Hiển thị những dòng đầu đã chọn" + +#: ../src/main-window.c:351 ../src/message-window.c:90 +msgid "All _Headers" +msgstr "_Mọi dòng đầu" + +#: ../src/main-window.c:351 +msgid "Display all headers" +msgstr "Hiển thị mọi dòng đầu" + +#: ../src/main-window.c:360 +msgid "Toggle flagged" +msgstr "Bật/tắt đặt cờ" + +#: ../src/main-window.c:366 +msgid "Toggle deleted flag" +msgstr "Bật tắt đặt cờ đã xoá bỏ" + +#: ../src/main-window.c:371 +msgid "Toggle New" +msgstr "Bật/tắt Mới" + +#: ../src/main-window.c:377 +msgid "Toggle Answered" +msgstr "Bật/tắt Đã trả lời" + +#: ../src/main-window.c:392 ../src/user_popup.c:67 +#, fuzzy +msgid "_Message..." +msgstr "" +"#-#-#-#-# balsa.po (balsa HEAD) #-#-#-#-#\n" +"_Thư...\n" +"#-#-#-#-# gnomeicu.po (gnomeicu HEAD) #-#-#-#-#\n" +"_Tin nhẳn..." + +#: ../src/main-window.c:392 ../src/main-window.c:1041 +msgid "Compose a new message" +msgstr "Soạn thảo thư mới" + +#. We could use GNOMEUIINFO_INCLUDE but it appends the menu instead +#. of including at specified position +#: ../src/main-window.c:399 +msgid "Local mbox mailbox..." +msgstr "Hộp thư mbox cục bộ..." + +#: ../src/main-window.c:400 ../src/main-window.c:813 +msgid "Add a new mbox style mailbox" +msgstr "Thêm một hộp thư kiểu mbox mới" + +#: ../src/main-window.c:403 +msgid "Local Maildir mailbox..." +msgstr "Hộp thư Maildir cục bộ..." + +#: ../src/main-window.c:404 ../src/main-window.c:818 +msgid "Add a new Maildir style mailbox" +msgstr "Thêm một hộp thư kiểu Maildir mới" + +#: ../src/main-window.c:407 +msgid "Local MH mailbox..." +msgstr "Hộp thư MH cục bộ..." + +#: ../src/main-window.c:408 ../src/main-window.c:822 +msgid "Add a new MH style mailbox" +msgstr "Thêm một hộp thư kiểu MH mới" + +#: ../src/main-window.c:411 ../src/main-window.c:825 +#: ../src/pref-manager.c:2970 +msgid "Remote IMAP mailbox..." +msgstr "Hộp thư IMAP ở xa..." + +#: ../src/main-window.c:412 ../src/main-window.c:826 +msgid "Add a new IMAP mailbox" +msgstr "Thêm một hộp thư kiểu IMAP mới" + +#: ../src/main-window.c:416 ../src/main-window.c:830 +#: ../src/pref-manager.c:2975 +msgid "Remote IMAP folder..." +msgstr "Thư mục IMAP ở xa..." + +#: ../src/main-window.c:417 ../src/main-window.c:831 +msgid "Add a new IMAP folder" +msgstr "Thêm một thư mục IMAP mới" + +#: ../src/main-window.c:420 ../src/main-window.c:834 +msgid "Remote IMAP subfolder..." +msgstr "Thư mục con IMAP ở xa..." + +#: ../src/main-window.c:421 ../src/main-window.c:835 +msgid "Add a new IMAP subfolder" +msgstr "Thêm một thư mục con IMAP mới" + +#: ../src/main-window.c:434 ../data/sound-juicer.glade.h:26 +#: ../src/sj-main.c:240 ../glade/straw.glade.h:71 +msgid "_Continue" +msgstr "_Tiếp tục" + +#: ../src/main-window.c:435 +msgid "Continue editing current message" +msgstr "Tiếp tục lại soạn thảo thư hiện có" + +#. Ctrl-M +#: ../src/main-window.c:442 ../src/main-window.c:877 +msgid "_Get New Mail" +msgstr "_Lấy thư mới" + +#: ../src/main-window.c:442 ../src/main-window.c:877 +msgid "Fetch new incoming mail" +msgstr "Lấy các thư mới được gởi đến" + +#. Ctrl-S +#: ../src/main-window.c:448 ../src/main-window.c:872 +msgid "_Send Queued Mail" +msgstr "_Gởi thư đang đợi" + +#: ../src/main-window.c:449 ../src/main-window.c:873 +msgid "Send messages from the outbox" +msgstr "Gởi các thư từ hộp Thư Đi" + +#. Ctrl-B +#: ../src/main-window.c:455 ../src/main-window.c:867 +msgid "Send and _Receive Mail" +msgstr "Gởi và _Nhận Thư" + +#: ../src/main-window.c:456 ../src/main-window.c:868 +#: ../src/toolbar-factory.c:113 +msgid "Send and Receive messages" +msgstr "Gởi và nhạn thư" + +#: ../src/main-window.c:461 ../src/main-window.c:882 +#: ../src/sendmsg-window.c:354 ../src/sendmsg-window.c:521 +#: ../plug-ins/print/print.c:172 +msgid "_Print..." +msgstr "_In..." + +#: ../src/main-window.c:462 ../src/main-window.c:883 +#: ../src/toolbar-factory.c:109 +msgid "Print current message" +msgstr "In thư hiện thời" + +#: ../src/main-window.c:468 ../src/main-window.c:1100 +msgid "_Address Book..." +msgstr "_Sổ địa chỉ..." + +#: ../src/main-window.c:469 ../src/main-window.c:1101 +msgid "Open the address book" +msgstr "Mở sổ địa chỉ" + +#: ../src/main-window.c:493 +msgid "F_ilters..." +msgstr "_Lọc..." + +#: ../src/main-window.c:493 ../src/main-window.c:1087 +msgid "Manage filters" +msgstr "Quản lý bộ lọc" + +#: ../src/main-window.c:495 ../src/main-window.c:1093 +msgid "_Export Filters" +msgstr "_Xuất bộ lọc" + +#: ../src/main-window.c:495 ../src/main-window.c:1094 +msgid "Export filters as Sieve scripts" +msgstr "Xuất ra các bộ lọc dạng tập lệnh Sieve" + +#: ../src/main-window.c:503 +msgid "_Flat index" +msgstr "Chỉ mục _phẳng" + +#: ../src/main-window.c:504 +msgid "No threading at all" +msgstr "Không có nhánh nào" + +#: ../src/main-window.c:509 +msgid "Si_mple threading" +msgstr "Nhánh đ_ơn giản" + +#: ../src/main-window.c:510 +msgid "Simple threading algorithm" +msgstr "Thuật toán đơn giản sắp xếp theo nhánh" + +#: ../src/main-window.c:515 +msgid "_JWZ threading" +msgstr "Nhánh _JWZ" + +#: ../src/main-window.c:516 +msgid "Elaborate JWZ threading" +msgstr "Sắp xếp theo JWZ phức tạp" + +#: ../src/main-window.c:526 +msgid "_Show Mailbox Tree" +msgstr "_Hiện cây hộp thư" + +#: ../src/main-window.c:527 +msgid "Toggle display of mailbox and folder tree" +msgstr "Hiện/Ẩn cây các hộp thư và thư mục" + +#: ../src/main-window.c:531 +msgid "Show Mailbox _Tabs" +msgstr "Hiện th_anh hộp thư" + +#: ../src/main-window.c:532 +msgid "Toggle display of mailbox notebook tabs" +msgstr "Hiện/Ẩn các thanh cuốn vở hộp thư" + +#: ../plug-ins/common/ripple.c:568 +msgid "_Wrap" +msgstr "_Cuộn" + +#: ../src/sendmsg-window.c:383 ../src/sendmsg-window.c:546 +msgid "Wrap message lines" +msgstr "Ngắt các dòng trong thư" + +#: ../src/quick-lounge.glade.h:11 +msgid "E_xpand All" +msgstr "_Bung hết" + +#: ../src/main-window.c:546 ../src/main-window.c:959 +msgid "Expand all threads" +msgstr "Bung mọi nhánh" + +#: ../src/main-window.c:550 ../src/main-window.c:963 +msgid "_Collapse All" +msgstr "Th_u gọn hết" + +#: ../src/main-window.c:551 ../src/main-window.c:964 +msgid "Collapse all expanded threads" +msgstr "Thu gọn mọi nhánh đã bung" + +#: src/gtkam-main.c:558 +msgid "Zoom _In" +msgstr "Phóng _to" + +#: ../src/main-window.c:557 ../src/main-window.c:994 +#: ../src/message-window.c:127 +msgid "Increase magnification" +msgstr "Phóng to vùng xem" + +#: src/gtkam-main.c:560 +msgid "Zoom _Out" +msgstr "Thu _nhỏ" + +#: ../src/main-window.c:561 ../src/main-window.c:998 +#: ../src/message-window.c:131 +msgid "Decrease magnification" +msgstr "Thu nhỏ vùng xem" + +#: ../src/message-window.c:138 +#, no-c-format +msgid "Zoom _100%" +msgstr "Phóng to 100%" + +#: ../src/main-window.c:567 ../src/main-window.c:1004 +#: ../src/message-window.c:138 +msgid "No magnification" +msgstr "Không phóng/thu" + +#: ../src/main-window.c:585 ../src/main-window.c:1048 +#: ../src/toolbar-factory.c:85 +msgid "Reply to the current message" +msgstr "Trả lời thư hiện thời" + +#. A +#: ../src/main-window.c:592 ../src/main-window.c:1054 +#: ../src/message-window.c:153 +msgid "Reply to _All..." +msgstr "Trả lời _mọi người..." + +#: ../src/main-window.c:593 ../src/main-window.c:1055 +msgid "Reply to all recipients of the current message" +msgstr "Soạn thư trả lời cho mọi người nhận thư được chọn" + +#: ../src/main-window.c:600 ../src/message-window.c:159 +msgid "Reply to _Group..." +msgstr "Trả lời _Nhóm.." + +#: ../src/main-window.c:601 ../src/message-window.c:160 +#: ../src/toolbar-factory.c:89 +msgid "Reply to mailing list" +msgstr "Trả lời Hộp thư chung" + +#: ../src/main-window.c:608 ../src/message-window.c:165 +msgid "_Forward attached..." +msgstr "Chuyển tiếp dạng đính _kèm..." + +#: ../src/main-window.c:609 +msgid "Forward the current message as attachment" +msgstr "Chuyển tiếp thư được chọn tới người khác như là đính kèm" + +#: ../src/main-window.c:615 +msgid "Forward _inline..." +msgstr "Chuyển tiếp trực t_iếp..." + +#: ../src/main-window.c:616 +msgid "Forward the current message inline" +msgstr "Chuyển tiếp thư được chọn tới người khác như là thân thư" + +#: ../src/main-window.c:624 +msgid "Pipe the message through another program" +msgstr "Gởi thư qua ống dẫn đến một chương trình khác" + +#: ../src/main-window.c:631 +msgid "_Next Part" +msgstr "Phần _kế" + +#: ../src/main-window.c:631 ../src/message-window.c:177 +msgid "Next part in message" +msgstr "Phần kế tiếp trong thư" + +#: ../src/main-window.c:637 +msgid "_Previous Part" +msgstr "Phần t_rước" + +#: ../src/main-window.c:638 ../src/message-window.c:183 +msgid "Previous part in message" +msgstr "Phần trước đó trong thư" + +#: ../src/main-window.c:644 ../src/main-window.c:1068 +#: ../src/message-window.c:187 +msgid "Save Current Part..." +msgstr "Lưu phần hiện có..." + +#: ../src/main-window.c:645 ../src/main-window.c:1069 +msgid "Save currently displayed part of message" +msgstr "Lưu phần thư hiện thời được hiển thị" + +#: ../src/main-window.c:651 ../src/main-window.c:1021 +#: ../src/message-window.c:192 +msgid "_View Source..." +msgstr "_Xem mã nguồn..." + +#: ../src/main-window.c:652 ../src/main-window.c:1022 +#: ../src/message-window.c:193 +msgid "View source form of the message" +msgstr "Xem thư dạng mã nguồn" + +#: ../src/main-window.c:670 ../src/message-window.c:228 +msgid "_Move to Trash" +msgstr "Chu_yển vào Rác" + +#: ../src/main-window.c:671 ../src/main-window.c:1076 +msgid "Move the current message to Trash mailbox" +msgstr "Chuyển thư hiện thời vào hộp thư Rác" + +#. ! +#: ../src/main-window.c:677 ../src/main-window.c:1027 +msgid "_Toggle flag" +msgstr "_Bật/tắt đặt cờ" + +#: ../src/main-window.c:683 ../src/main-window.c:1032 +msgid "Store address of sender in addressbook" +msgstr "Lưu địa chỉ của người gởi vào sổ địa chỉ" + +#: ../src/main-window.c:694 ../src/main-window.c:985 +msgid "Next Message" +msgstr "Thư kế" + +#: ../src/main-window.c:700 ../src/main-window.c:989 +#: ../src/message-window.c:207 +msgid "Previous Message" +msgstr "Thư trước" + +#: ../src/main-window.c:706 ../src/main-window.c:707 ../src/main-window.c:979 +#: ../src/main-window.c:980 ../src/message-window.c:213 +#: ../src/message-window.c:214 +msgid "Next Unread Message" +msgstr "Thư chưa đọc kế" + +#: ../src/main-window.c:713 ../src/main-window.c:714 ../src/main-window.c:944 +#: ../src/main-window.c:945 ../src/message-window.c:220 +#: ../src/message-window.c:221 +msgid "Next Flagged Message" +msgstr "Thư đã đặt cờ kế" + +#: ../src/main-window.c:721 +msgid "_Hide messages" +msgstr "Ẩ_n thư" + +#: ../src/main-window.c:723 +msgid "_Reset Filter" +msgstr "Đặt _lại bộ lọc" + +#: ../src/main-window.c:723 +msgid "Reset mailbox filter" +msgstr "Đặt lại bộ lọc hộp thư" + +#: ../data/sound-juicer.glade.h:36 ../src/yelp-window.c:320 +#: ../src/glade-editor.c:1050 ../src/form-win.cc:381 ../src/form-win.cc:443 +msgid "_Select All" +msgstr "_Chọn hết" + +#: ../src/main-window.c:729 +msgid "Select all messages in current mailbox" +msgstr "Chọn mọi thư trong hộp thư hiện có" + +#. #-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-# +#. Custom editor button +#. +#: ../ui/muds.glade.h:50 ../glade/pyblio.glade.in.h:17 +#: ../src/glade-editor.c:117 +msgid "_Edit..." +msgstr "_Hiệu chỉnh..." + +#: ../src/main-window.c:735 ../src/main-window.c:845 +msgid "Edit the selected mailbox" +msgstr "Hiệu chỉnh hộp thư đã chọn" + +#: ../src/main-window.c:739 ../src/main-window.c:840 +#: ../glade/pyblio.glade.in.h:16 +msgid "_Delete..." +msgstr "_Xoá bỏ..." + +#: ../src/main-window.c:740 ../src/main-window.c:841 +msgid "Delete the selected mailbox" +msgstr "Xoá bỏ hộp thư đã chọn" + +#: ../src/main-window.c:746 +msgid "E_xpunge Deleted Messages" +msgstr "_Xoá hẳn các thư đã xoá bỏ" + +#: ../src/main-window.c:747 ../src/main-window.c:852 +msgid "Expunge messages marked as deleted in the currently opened mailbox" +msgstr "Xoá hẳn các thư có nhãn « Đã xoá bỏ » trong hộp thư hiện thời đang mở" + +#: ../src/main-window.c:752 ../src/main-window.c:858 +msgid "Close mailbox" +msgstr "Đóng hộp thư" + +#: ../src/nautilus-information-panel.c:1039 +msgid "Empty _Trash" +msgstr "Đổ _Rác" + +#: ../src/toolbar-factory.c:135 +msgid "Delete messages from the Trash mailbox" +msgstr "Xoá bỏ thư ra hộp thư Rác" + +#: ../src/main-window.c:761 +msgid "Select _Filters" +msgstr "Chọn bộ _lọc" + +#: ../src/main-window.c:762 ../src/main-window.c:1091 +msgid "Select filters to be applied automatically to current mailbox" +msgstr "Chọn các bộ lọc cần tự động áp dụng cho hộp thư hiện thời" + +#: ../src/main-window.c:767 +msgid "_Remove Duplicates" +msgstr "_Gỡ bỏ thư trùng" + +#: ../src/main-window.c:768 +msgid "Remove duplicated messages from the selected mailbox" +msgstr "Gỡ bỏ các thư trùng ra hộp thư được chọn" + +#: ../src/main-window.c:783 +msgid "_Toolbars..." +msgstr "Thanh _công cụ..." + +#: ../src/main-window.c:784 ../src/ephy-window.c:191 +msgid "Customize toolbars" +msgstr "Tùy chỉnh thanh công cụ" + +#: ../src/main-window.c:787 ../src/main-window.c:1109 +msgid "_Identities..." +msgstr "_Thực thể..." + +#: ../src/main-window.c:788 ../src/main-window.c:1110 +msgid "Create and set current identities" +msgstr "Tạo và đặt thực thể hiện thời" + +#: ../src/main-window.c:799 +msgid "Mail_box" +msgstr "_Hộp thư" + +#: ../src/main-window.c:800 ../src/main-window.c:1123 +#: ../src/message-window.c:245 ../ui/evolution-mail-list.xml.h:27 +#: ../ui/evolution-mail-message.xml.h:110 +msgid "_Message" +msgstr "_Thư" + +#: ../src/main-window.c:812 +msgid "New mailbox..." +msgstr "Hộp thư mới..." + +#: ../src/main-window.c:817 +msgid "New \"Maildir\" mailbox..." +msgstr "Hộp thư Maildir mới..." + +#: ../src/main-window.c:821 +msgid "New \"MH\" mailbox..." +msgstr "Hộp thư MH mới..." + +#: ../src/main-window.c:851 +msgid "_Compress Mailbox" +msgstr "_Nén hộp thư" + +#: ../src/main-window.c:881 +msgid "Mail_boxes" +msgstr "_Hộp thư" + +#: ../src/main-window.c:906 +msgid "By _Arrival" +msgstr "Theo giờ _đến" + +#: ../src/main-window.c:906 +msgid "Arrival order" +msgstr "Thứ tự đến" + +#: ../src/main-window.c:910 +msgid "By _Sender" +msgstr "Theo người _gởi" + +#: ../src/main-window.c:910 +msgid "Sender order" +msgstr "Thứ tự người gởi" + +#: ../src/main-window.c:914 +msgid "By S_ubject" +msgstr "Theo _chủ đề" + +#: ../src/main-window.c:914 +msgid "Subject order" +msgstr "Thứ tự chủ đề" + +#: ../src/main-window.c:918 +msgid "By Si_ze" +msgstr "Theo c_ỡ" + +#: ../src/main-window.c:918 +msgid "By message size" +msgstr "Theo kích cỡ thư" + +#: ../src/main-window.c:922 +msgid "_Threaded" +msgstr "_Nhánh" + +#: ../src/main-window.c:922 +msgid "Use message threading" +msgstr "Sắp xếp thư theo nhánh" + +#: ../src/main-window.c:930 ../gtk/gtkstock.c:405 +msgid "_Descending" +msgstr "_Giảm dần" + +#: ../src/main-window.c:930 +msgid "Sort in a descending order" +msgstr "Sắp xếp giảm dần" + +#: ../src/main-window.c:951 +msgid "_Headers" +msgstr "_Dòng đầu" + +#: ../src/main-window.c:954 +msgid "_Sort Mailbox" +msgstr "_Sắp xếp hộp thư" + +#: ../src/main-window.c:955 +msgid "H_ide messages" +msgstr "Ẩ_n thư" + +#: ../src/main-window.c:969 +msgid "_View filter" +msgstr "_Xem bộ lọc" + +#: ../src/main-window.c:970 +msgid "Enable quick message index filter" +msgstr "Bật bộ lọc chỉ mục thư nhanh" + +#: ../src/sendmsg-window.c:645 ../desktop/client/src/connect.c:454 +msgid "_More" +msgstr "Th_êm" + +#: ../src/main-window.c:1041 ../app/actions/image-actions.c:71 +#: ../app/actions/image-actions.c:76 +msgid "_New..." +msgstr "_Mới..." + +#. F +#: ../src/main-window.c:1061 +msgid "_Forward..." +msgstr "Chu_yển tiếp..." + +#: ../src/main-window.c:1062 ../src/toolbar-factory.c:91 +msgid "Forward the current message" +msgstr "Chuyển tiếp thư hiện có" + +#. D +#: ../src/main-window.c:1075 +msgid "_Delete to Trash" +msgstr "_Xoá bỏ vào Rác" + +#: ../src/main-window.c:1087 +msgid "_Manage..." +msgstr "_Quản lý..." + +#: ../src/main-window.c:1090 +msgid "_Select Filters" +msgstr "_Chọn bộ lọc" + +#: ../src/main-window.c:1108 +msgid "_Filters" +msgstr "_Lọc" + +#: ../src/mlview-app.cc:240 +msgid "_Tools" +msgstr "_Công cụ" + +#: ../src/main-window.c:1228 +msgid "" +"Balsa is sending a mail now.\n" +"Abort sending?" +msgstr "" +"Balsa hiện đang gởi thư.\n" +"Muốn hủy việc gởi thư?" + +#: ../src/main-window.c:1448 +msgid "Subject or Sender Contains:" +msgstr "Chủ đề hay Người gởi chứa:" + +#: ../src/main-window.c:1449 +msgid "Subject or Recipient Contains:" +msgstr "Chủ đề hay Người nhận chứa:" + +#: ../src/main-window.c:1450 +msgid "Subject Contains:" +msgstr "Chủ đề chứa:" + +#: ../src/main-window.c:1451 +msgid "Body Contains:" +msgstr "Thân chứa:" + +#: ../src/main-window.c:1452 +msgid "Older than (days):" +msgstr "Cũ hơn (ngày):" + +#: ../src/main-window.c:2331 +#, c-format +msgid "" +"Unable to Open Mailbox!\n" +"%s." +msgstr "" +"• Không thể mở hộp thư. •\n" +"%s." + +#: ../gnome/applet/applet.c:356 ../pan/dialogs/dialog-about.c:86 +#: ../gnome/applet/applet.c:329 +msgid "translator-credits" +msgstr "Nhóm Việt hóa Gnome " + +#: ../src/main-window.c:2656 ../src/main-window.c:2683 +msgid "" +"The Balsa email client is part of the GNOME desktop environment. " +"Information on Balsa can be found at http://balsa.gnome.org/\n" +"\n" +"If you need to report bugs, please do so at: http://bugzilla.gnome.org/" +msgstr "" +"Ứng dụng khách thư điện tử Balsa là một phần của môi trường Gnome. Thông " +"tin về Balsa có ở \n" +"\n" +"Nếu bạn muốn thông báo lỗi, hãy thực hiện tại ." + +#: ../src/main-window.c:2787 ../src/main-window.c:2800 +msgid "Checking Mail..." +msgstr "Đang kiểm tra thư..." + +#: ../src/main-window.c:2978 +#, c-format +msgid "IMAP mailbox: %s" +msgstr "Hộp thư IMAP: %s" + +#: ../src/main-window.c:2981 +#, c-format +msgid "Local mailbox: %s" +msgstr "Hộp thư cục bộ : %s" + +#: ../src/main-window.c:3119 +msgid "Finished Checking." +msgstr "Mới kiểm tra xong." + +#: ../src/main-window.c:3181 +#, c-format +msgid "Sending error: %s" +msgstr "Lỗi gởi : %s" + +#: ../src/main-window.c:3266 +msgid "Balsa: New mail" +msgstr "Balsa: Thư mới" + +#: ../src/main-window.c:3278 +#, c-format +msgid "You have received %d new message." +msgid_plural "You have received %d new messages." +msgstr[0] "Bạn đã nhận %d thư mới." + +#: ../src/main-window.c:3281 libmisc/mail.c:62 libmisc/mail.c:77 +#: libmisc/mail.c:61 libmisc/mail.c:76 +msgid "You have new mail." +msgstr "Bạn có thư mới." + +#: ../src/main-window.c:3405 +#, c-format +msgid "The next unread message is in %s" +msgstr "Thư chưa đọc kế tiếp có trong %s" + +#: ../src/main-window.c:3409 +#, c-format +msgid "Do you want to switch to %s?" +msgstr "Bạn có muốn chuyển đổi sang %s không?" + +#: ../src/main-window.c:3939 +msgid "Search mailbox" +msgstr "Tìm kiếm trong hộp thư" + +#: ../src/main-window.c:3956 ../gdictsrc/gdict-app.c:401 src/mainwin.cpp:1219 +msgid "_Search for:" +msgstr "Tìm _kiếm:" + +#. builds the toggle buttons to specify fields concerned by +#. * the search. +#: ../src/main-window.c:3969 +msgid "In:" +msgstr "Trong:" + +#: ../src/main-window.c:3980 +msgid "S_ubject" +msgstr "_Chủ đề" + +#. Frame with Apply and Clear buttons +#: ../src/main-window.c:3985 +msgid "Show only matching messages" +msgstr "Hiển thị chỉ những thư khớp" + +#. Frame with OK button +#: ../src/main-window.c:4006 +msgid "Open next matching message" +msgstr "Mở thư khớp kế tiếp" + +#: ../src/main-window.c:4017 +msgid "_Reverse search" +msgstr "_Đảo hướng tìm kiếm" + +#: ../src/main-window.c:4022 ../glade2/filediff.glade.h:15 +#: ../app/dialogs/offset-dialog.c:215 ../plug-ins/common/papertile.c:349 +msgid "_Wrap around" +msgstr "_Cuộn vòng" + +#: ../src/main-window.c:4184 +msgid "You can apply filters only on mailbox\n" +msgstr "Có thể áp dụng các bộ lọc chỉ cho hộp thư\n" + +#: ../src/main-window.c:4198 +#, c-format +msgid "Removing duplicates failed: %s" +msgstr "Việc gỡ bỏ thư trùng bị lỗi: %s" + +#: ../src/main-window.c:4418 +#, c-format +msgid "Could not open trash: %s" +msgstr "Không thể mở Rác: %s" + +#: ../src/main-window.c:4547 +#, c-format +msgid "Balsa: %s (readonly)" +msgstr "Balsa: %s (chỉ đọc)" + +# Name: don't translate / Tên: đừng dịch +#: ../src/main-window.c:4549 +#, c-format +msgid "Balsa: %s" +msgstr "Balsa: %s" + +#: ../src/main.c:212 ../src/main.c:260 +msgid "Get new mail on startup" +msgstr "Lấy thư mới khi khởi chạy" + +#: ../src/main.c:214 ../src/main.c:262 +msgid "Compose a new email to EMAIL@ADDRESS" +msgstr "Biên soạn một thư mới cho TÊN@ĐỊA_CHỈ" + +#: ../src/main.c:216 ../src/main.c:264 +msgid "Attach file at PATH" +msgstr "Đính kèm tập tin tại ĐƯỜNG_DẪN" + +#: ../src/main.c:218 ../src/main.c:267 +msgid "Opens MAILBOXNAME" +msgstr "Mở TÊN_HỘP_THƯ" + +#: ../src/main.c:218 ../src/main.c:267 +msgid "MAILBOXNAME" +msgstr "TÊN_HỘP_THƯ" + +#: ../src/main.c:221 ../src/main.c:270 +msgid "Opens first unread mailbox" +msgstr "Mở hộp thư chưa đọc đầu tiên" + +#: ../src/main.c:224 ../src/main.c:273 +msgid "Opens default Inbox on startup" +msgstr "Mở hộp Thư Đến mặc định khi khởi chạy" + +#: ../src/main.c:227 ../src/main.c:276 +msgid "Prints number unread and unsent messages" +msgstr "In số thư chưa đọc/gởi" + +#: ../src/main.c:229 ../src/main.c:278 +msgid "Debug POP3 connection" +msgstr "Gỡ lỗi kết nối POP3" + +#: ../src/main.c:231 ../src/main.c:280 +msgid "Debug IMAP connection" +msgstr "Gỡ lỗi kết nối IMAP" + +#: ../src/main.c:253 +msgid "The Balsa E-Mail Client" +msgstr "Ứng dụng khách thư điện tử Balsa" + +#: ../src/main.c:345 +#, c-format +msgid "Balsa cannot open your \"%s\" mailbox." +msgstr "Balsa: không thể mở hộp thư « %s » của bạn." + +#: ../src/main.c:322 ../storage/exchange-hierarchy-foreign.c:254 +msgid "Inbox" +msgstr "Thư Đến" + +#: ../src/main.c:328 ../storage/exchange-hierarchy-foreign.c:257 +msgid "Outbox" +msgstr "Thư Đi" + +#: ../src/main.c:334 +msgid "Sentbox" +msgstr "Đã gởi" + +#: ../src/main.c:340 +msgid "Draftbox" +msgstr "Nháp" + +#: ../src/main.c:345 ../libnautilus-private/nautilus-trash-directory.c:343 +msgid "Trash" +msgstr "Rác" + +#: ../src/main.c:562 +msgid "Compressing mail folders..." +msgstr "Đang nén thư mục thư..." + +#: ../src/message-window.c:86 +msgid "N_o Headers" +msgstr "Ẩ_n dòng đầu" + +#: ../src/message-window.c:88 +msgid "_Selected Headers" +msgstr "Dòng đầu đã _chọn" + +#: ../src/message-window.c:148 +msgid "Reply to this message" +msgstr "Trả lời thư này" + +#: ../src/message-window.c:154 +msgid "Reply to all recipients of this message" +msgstr "Trả lời mọi người nhận thư này" + +#: ../src/message-window.c:166 +msgid "Forward this message as attachment" +msgstr "Chuyển tiếp thư này tới người khác dạng đính kèm" + +#: ../src/message-window.c:170 +msgid "Forward inline..." +msgstr "Chuyển tiếp trực tiếp..." + +#: ../src/message-window.c:171 +msgid "Forward this message inline" +msgstr "Chuyển tiếp thư này tới người khác dạng thân thư" + +#: ../src/message-window.c:177 +msgid "Next Part" +msgstr "Phần kế" + +#: ../src/message-window.c:182 +msgid "Previous Part" +msgstr "Phần trước" + +#: ../src/message-window.c:188 +msgid "Save current part in message" +msgstr "Lưu phần hiện thời trong thư" + +#: ../src/message-window.c:200 ../ui/evolution-mail-message.xml.h:117 +#: ../ui/evolution-mail-message.xml.h:112 +msgid "_Next Message" +msgstr "Thư _kế" + +#: ../src/message-window.c:200 +msgid "Next message" +msgstr "Thư kế tiếp" + +#: ../src/message-window.c:206 ../ui/evolution-mail-message.xml.h:121 +#: ../ui/evolution-mail-message.xml.h:116 +msgid "_Previous Message" +msgstr "Thư t_rước" + +#: ../src/message-window.c:229 +msgid "Move the message to Trash mailbox" +msgstr "Chuyển thư vào hộp thư Rác" + +#: ../src/message-window.c:244 +msgid "M_ove" +msgstr "Chu_yển" + +#: ../src/message-window.c:366 +#, c-format +msgid "Message from %s: %s" +msgstr "Thừ từ %s: %s" + +#: ../src/balsa-mime-widget-callbacks.c:50 +#: ../src/balsa-mime-widget-callbacks.c:114 +#, c-format +msgid "Could not create temporary file %s: %s" +msgstr "Không thể tạo tập tin tạm thời %s: %s" + +#: ../src/balsa-mime-widget-callbacks.c:140 +#, c-format +msgid "Save %s MIME Part" +msgstr "Lưu phần MIME %s" + +#: ../src/balsa-mime-widget-callbacks.c:187 +msgid "File already exists. Overwrite?" +msgstr "Tập tin đã có, ghi đè không?" + +#: ../src/balsa-mime-widget-crypto.c:70 +#, c-format +msgid "" +"This is an inline %s signed %s message part:\n" +"%s" +msgstr "" +"Đây là phần thư trực tiếp %s có chữ ký %s:\n" +"%s" + +# Name: don't translate / Tên: đừng dịch +#: ../src/balsa-mime-widget-crypto.c:72 ../src/print.c:1302 +msgid "OpenPGP" +msgstr "OpenPGP" + +#: ../src/balsa-mime-widget-crypto.c:89 +msgid "_Run gpg to import this key" +msgstr "_Chạy GPG để nhập khoá này" + +#: ../src/balsa-mime-widget-image.c:51 ../src/balsa-mime-widget-image.c:156 +#, c-format +msgid "Error loading attached image: %s\n" +msgstr "Gặp lỗi khi tải ảnh đính kèm: %s\n" + +#: ../src/balsa-mime-widget-message.c:164 +#: ../src/balsa-mime-widget-message.c:177 +#: ../src/balsa-mime-widget-message.c:205 +#: ../src/balsa-mime-widget-message.c:258 +msgid "Content Type: external-body\n" +msgstr "Kiểu nội dung: thân bên ngoài\n" + +#: ../src/balsa-mime-widget-message.c:165 +msgid "Access type: local-file\n" +msgstr "Kiểu truy cập: tập tin cục bộ\n" + +#: ../src/balsa-mime-widget-message.c:166 +#: ../src/balsa-mime-widget-message.c:213 ../src/balsa-mime-widget.c:236 +#, c-format +msgid "File name: %s" +msgstr "Tên tập tin: %s" + +#: ../src/balsa-mime-widget-message.c:178 +msgid "Access type: URL\n" +msgstr "Kiểu truy cập: địa chỉ Mạng\n" + +#: ../src/balsa-mime-widget-message.c:179 ../calendar/gui/print.c:2423 +#: ../calendar/gui/print.c:2412 ../plug-ins/imagemap/imap_main.c:1061 +#, c-format +msgid "URL: %s" +msgstr "Địa chỉ Mạng: %s" + +#: ../src/balsa-mime-widget-message.c:206 +#, c-format +msgid "Access type: %s\n" +msgstr "Kiểu truy cập: %s\n" + +#: ../src/balsa-mime-widget-message.c:210 +#, c-format +msgid "FTP site: %s\n" +msgstr "Chỗ Mạng FTP: %s\n" + +#: ../src/balsa-mime-widget-message.c:212 +#, c-format +msgid "Directory: %s\n" +msgstr "Thư mục: %s\n" + +#: ../src/balsa-mime-widget-message.c:259 +msgid "Access type: mail-server\n" +msgstr "Kiểu truy cập: máy phục vụ thư\n" + +#: ../src/balsa-mime-widget-message.c:260 +#, c-format +msgid "Mail server: %s\n" +msgstr "Máy phục vụ thư : %s\n" + +#: ../src/balsa-mime-widget-message.c:262 +#, c-format +msgid "Subject: %s\n" +msgstr "Chủ đề: %s\n" + +#: ../src/balsa-mime-widget-message.c:279 +msgid "Se_nd message to obtain this part" +msgstr "Gởi thư để lấy phần này" + +#: ../src/balsa-mime-widget-message.c:300 ../src/balsa-mime-widget-text.c:675 +#: ../src/balsa-mime-widget-text.c:866 ../src/sendmsg-window.c:1762 +#, c-format +msgid "Error showing %s: %s\n" +msgstr "Gặp lỗi khi hiển thị %s: %s\n" + +#: ../src/balsa-mime-widget-message.c:332 ../src/print.c:675 +#, c-format +msgid "Could not get a part: %s" +msgstr "Không thể lấy phần: %s" + +#: ../src/balsa-mime-widget-message.c:601 ../src/print.c:350 +#: ../src/sendmsg-window.c:1105 ../src/sendmsg-window.c:3310 +#: ../src/sendmsg-window.c:5068 ../xpdf/gpdf-properties-dialog.glade.h:11 +msgid "Subject:" +msgstr "Chủ đề:" + +#: ../src/lib/ItemView.py:357 Expense/expense.c:1739 +msgid "Date:" +msgstr "Ngày:" + +#: ../plug-ins/xslt/xsltdialog.c:111 ../glade/medline.glade.h:3 +#: src/splash.c:806 +msgid "From:" +msgstr "Từ :" + +#: ../src/balsa-mime-widget-message.c:618 ../src/sendmsg-window.c:1102 +msgid "Reply-To:" +msgstr "Trả lời:" + +#: ../glade/medline.glade.h:16 +msgid "To:" +msgstr "" +"#-#-#-#-# balsa.po (balsa HEAD) #-#-#-#-#\n" +"Cho :\n" +"#-#-#-#-# Compendium03.po (apt) #-#-#-#-#\n" +"Đến:\n" +"#-#-#-#-# dia.po (dia HEAD) #-#-#-#-#\n" +"Đến:\n" +"#-#-#-#-# drivel.po (drivel HEAD) #-#-#-#-#\n" +"Đến:\n" +"#-#-#-#-# pybliographer.po (pybliographer.v_1_0_x) #-#-#-#-#\n" +"Đến:" + +#: ../src/bug-buddy.glade.h:21 +msgid "Cc:" +msgstr "Chép cho:" + +#: ../src/store-address.c:308 +msgid "Bcc:" +msgstr "Bcc:" + +# Literal: don't translate / Nghĩa chữ : đừng dịch +#: ../src/balsa-mime-widget-message.c:627 ../src/print.c:368 +msgid "Fcc:" +msgstr "Fcc:" + +#: ../src/balsa-mime-widget-message.c:634 ../src/print.c:374 +msgid "Disposition-Notification-To:" +msgstr "Thông báo chuyển nhượng:" + +#: ../src/balsa-mime-widget-text.c:115 +#, c-format +msgid "Could not save a text part: %s" +msgstr "Không thể lưu phần văn bản: %s" + +#: ../src/balsa-mime-widget-text.c:150 +#, c-format +msgid "" +"The message sent by %s with subject \"%s\" contains 8-bit characters, but no " +"header describing the used codeset (converted to %s)" +msgstr "" +"Thư được gởi bởi %s với chủ đề « %s » chứa ký tự 8-bit, nhưng không có dòng " +"đầu diễn tả bộ ký tự đã dùng, nên đã chuyển đổi sang %s)." + +#: ../src/balsa-mime-widget-text.c:447 +msgid "Highlight structured phrases" +msgstr "Tô sáng các cụm từ có cấu trúc" + +#: ../src/balsa-mime-widget-text.c:665 +#, c-format +msgid "Calling URL %s..." +msgstr "Đang gọi địa chỉ Mạng %s..." + +#: ../src/balsa-mime-widget.c:255 +#, c-format +msgid "Error reading message part: %s" +msgstr "Gặp lỗi khi đọc phần thư : %s" + +#: ../src/balsa-mime-widget.c:279 +#, c-format +msgid "Type: %s (%s)" +msgstr "Kiểu : %s (%s)" + +#: ../src/balsa-mime-widget.c:282 +#, c-format +msgid "Content Type: %s" +msgstr "Kiểu nội dung: %s" + +#: ../src/balsa-mime-widget.c:296 +msgid "No open or view action defined in GNOME MIME for this content type" +msgstr "" +"Không có hành động mở hay xem được chỉ định trong GNOME MIME cho kiểu nội " +"dung này." + +#: ../src/balsa-mime-widget.c:302 +msgid "S_ave part" +msgstr "_Lưu phần" + +#: ../src/balsa-mime-widget.c:324 ../src/balsa-mime-widget.c:348 +#, c-format +msgid "View _part with %s" +msgstr "Xem _phần bằng %s" + +#: ../src/pref-manager.c:360 +msgid "While Retrieving Messages" +msgstr "Trong khi lấy thư" + +#: ../src/pref-manager.c:361 +msgid "Until Closed" +msgstr "Đến khi đã đóng" + +#: ../src/pref-manager.c:367 ../gtik/gtik.c:1415 +msgid "Fast" +msgstr "Nhanh" + +#: ../glade/glade_menu_editor.c:1068 ../glade/property.c:102 +#: ../src/glade-gtk.c:2356 ../widgets/gtk+.xml.in.h:127 +#: libexif/exif-entry.c:409 libexif/exif-entry.c:412 libexif/exif-entry.c:413 +#: libexif/exif-entry.c:414 libexif/exif-entry.c:473 +#: libexif/olympus/mnote-olympus-entry.c:103 +#: libexif/olympus/mnote-olympus-entry.c:148 +#: libexif/olympus/mnote-olympus-entry.c:154 +#: libexif/pentax/mnote-pentax-entry.c:92 +#: libexif/pentax/mnote-pentax-entry.c:97 +#: libexif/pentax/mnote-pentax-entry.c:102 +msgid "Normal" +msgstr "Chuẩn" + +#: ../src/pref-manager.c:369 +msgid "Bad Spellers" +msgstr "Người chính tả sai" + +#: ../src/pref-manager.c:375 +msgid "Message number" +msgstr "Số thứ tự thư" + +#: ../src/pref-manager.c:379 ../mail/em-filter-i18n.h:56 +msgid "Sender" +msgstr "Người gởi" + +#: ../src/pref-manager.c:383 +msgid "Flat" +msgstr "Phẳng" + +# Name: don't translate / Tên: đừng dịch +#: ../src/pref-manager.c:385 +msgid "JWZ" +msgstr "JWZ" + +#. must NOT be modal +#: ../src/pref-manager.c:436 +msgid "Balsa Preferences" +msgstr "Tùy thích Balsa" + +#: ../src/pref-manager.c:459 +msgid "Mail Servers" +msgstr "Máy phục vụ thư" + +#: ../src/pref-manager.c:463 ../src/pref-manager.c:2595 +msgid "Address Books" +msgstr "Sổ địa chỉ" + +#: ../src/pref-manager.c:467 +msgid "Mail Options" +msgstr "Tùy chọn thư" + +#: ../objects/FS/function.c:1060 ../sheets/Flowchart.sheet.in.h:6 +msgid "Display" +msgstr "Hiển thị" + +#: ../src/pref-manager.c:476 ../src/toolbar-factory.c:120 +msgid "Spelling" +msgstr "Chính tả" + +#: ../src/pref-manager.c:481 ../app/interface.c:973 +#: ../sheets/Misc.sheet.in.h:3 ../app/interface.c:985 ../app/interface.c:998 +#: app/midi-settings-050.c:587 app/midi-settings-09x.c:590 +msgid "Misc" +msgstr "Lặt vặt" + +#: ../src/pref-manager.c:485 +msgid "Startup" +msgstr "Khởi chạy" + +#: ../src/pref-manager.c:1223 +#, c-format +msgid "%s (default)" +msgstr "%s (mặc định)" + +#: ../src/pref-manager.c:1470 +msgid "Remote Mailbox Servers" +msgstr "Máy phục vụ hộp thư ở xa" + +#: ../glade/fields1.glade.h:20 ../src/glade-gtk.c:73 ../src/glade-gtk.c:3530 +#: ../mimedir/mimedir-vcard-email.c:149 schroot/sbuild-chroot.cc:388 +msgid "Type" +msgstr "Kiểu" + +#: ../src/pref-manager.c:1499 +msgid "Mailbox Name" +msgstr "Tên hộp thư" + +#: ../gnopi/gnopi_files/User_Properties/user_properties.glade2.h:28 +msgid "_Modify" +msgstr "_Sửa đổi" + +#: ../src/pref-manager.c:1524 +msgid "Local Mail" +msgstr "Thư cục bộ" + +#: ../src/pref-manager.c:1527 ../src/pref-manager.c:1535 +msgid "Select your local mail directory" +msgstr "Chọn thư mục thư cục bộ" + +#: ../src/pref-manager.c:1563 +msgid "Outgoing Mail Servers" +msgstr "Máy phục vụ thư gởi đi" + +#: ../src/pref-manager.c:1585 src/common/text.c:729 src/common/text.c:846 +#: src/common/text.c:888 +msgid "Server Name" +msgstr "Tên máy phục vụ" + +#: ../src/pref-manager.c:1622 ../filter/filter-rule.c:978 +#: ../filter/filter.glade.h:3 ../mail/em-utils.c:347 ../mail/em-utils.c:291 +msgid "Incoming" +msgstr "Gởi đến" + +#: ../src/pref-manager.c:1624 ../filter/filter-rule.c:978 +#: ../mail/em-utils.c:348 ../mail/em-utils.c:292 +msgid "Outgoing" +msgstr "Gởi đi" + +#: ../src/pref-manager.c:1648 +msgid "Checking" +msgstr "Kiểm tra" + +#: ../src/pref-manager.c:1653 +msgid "_Check mail automatically every:" +msgstr "Tự động _kiểm tra thư mỗi:" + +#: ../ui/prefs.glade.h:41 +msgid "minutes" +msgstr "phút" + +#: ../src/pref-manager.c:1670 +msgid "Check _IMAP mailboxes" +msgstr "Kiểm tra các hộp thư _IMAP" + +#: ../src/pref-manager.c:1676 +msgid "Check INBOX _only" +msgstr "Chỉ kiểm tra hộp Thư _Đến" + +#: ../src/pref-manager.c:1681 +msgid "Display message if new mail has arrived in an open mailbox" +msgstr "Hiển thị thư nếu có thư mới đến trong hộp thư đang mở" + +#: ../src/pref-manager.c:1686 +msgid "Do background check quietly (no messages in status bar)" +msgstr "" +"Chạy kiểm tra ở nền một cách thầm lặng (không hiển thị thư lên thanh trạng " +"thái)" + +#: ../src/pref-manager.c:1690 +msgid "_POP message size limit:" +msgstr "Hạn chế kích cỡ thư _POP:" + +#. Quoted text regular expression +#. and RFC2646-style flowed text +#: ../src/pref-manager.c:1716 +msgid "Quoted and Flowed Text" +msgstr "Văn bản trôi chảy và trích dẫn" + +#: ../src/pref-manager.c:1720 ../src/sendmsg-window.c:5636 +msgid "Quoted Text Regular Expression" +msgstr "Biểu thức chính quy cho văn bản trích dẫn" + +#: ../src/pref-manager.c:1731 +msgid "Wrap Incoming Text at:" +msgstr "Cuộn văn bản gởi đến tại:" + +#: ../src/pref-manager.c:1742 ../src/pref-manager.c:1905 +#: ../data/prefs-dialog.glade.h:9 +#: ../network-utilities/gnome-remote-shell.glade.h:18 +msgid "characters" +msgstr "ký tự" + +#. handling of multipart/alternative +#: ../src/pref-manager.c:1757 +msgid "Display of Multipart/Alternative Parts" +msgstr "Hiện thị phần Đa phần/Xen kẽ" + +#: ../src/pref-manager.c:1760 +msgid "prefer text/plain over html" +msgstr "thích chữ thô hơn HTML" + +#. treatment of messages with 8-bit chars, but without proper MIME encoding +#: ../src/pref-manager.c:1776 +msgid "National (8-bit) characters in broken messages without codeset header" +msgstr "" +"Ký tự thuộc quốc gia (8-bit) trong thư bị hỏng không có dòng đầu bộ ký tự" + +#: ../src/pref-manager.c:1782 +msgid "display as \"?\"" +msgstr "hiển thị dạng « ? »" + +#: ../src/pref-manager.c:1791 +msgid "display using codeset" +msgstr "hiển thị bằng bộ ký tự" + +#. How to handle received MDN requests +#: ../src/pref-manager.c:1823 +msgid "Message Disposition Notification Requests" +msgstr "Yêu cầu thông báo cách chuyển nhượng thư" + +#: ../src/pref-manager.c:1825 +msgid "" +"When I receive a message and its sender requested to return a\n" +"Message Disposition Notification (MDN), send it in the following cases:" +msgstr "" +"Khi nhận thư mà người gởi nó yêu cầu\n" +"Thông báo cách chuyển nhượng thư (MDN),\n" +"hãy gởi nó trong các trường hợp sau đây:" + +#: ../src/pref-manager.c:1836 +msgid "" +"The message header looks clean\n" +"(the notify-to address is equal to the return path,\n" +"I am in the \"To:\" or \"Cc:\" list)." +msgstr "" +"Dòng đầu của thư có vẻ sạch\n" +"(địa chỉ Thông Báo đến tương đương với đường dẫn trở lại.\n" +"Tôi đang ở danh sách « Cho » hay « Chép Cho »)." + +#: ../src/pref-manager.c:1851 +msgid "The message header looks suspicious." +msgstr "Dòng đầu của thư có vẻ đáng ngờ." + +#: ../src/pref-manager.c:1886 ../gtk/gtktext.c:630 +msgid "Word Wrap" +msgstr "Ngắt từ" + +#: ../src/pref-manager.c:1891 +msgid "Wrap Outgoing Text at:" +msgstr "Cuộn văn bản gởi đi tại:" + +#: ../src/pref-manager.c:1919 ../plug-ins/common/diffraction.c:641 +msgid "Other Options" +msgstr "Tùy chọn khác" + +#: ../src/pref-manager.c:1924 +msgid "Reply Prefix:" +msgstr "Tiền tố trả lời:" + +#: ../src/pref-manager.c:1927 +msgid "Edit headers in external editor" +msgstr "Sửa đổi dòng đầu trong bộ hiệu chỉnh nội bộ" + +#: ../src/pref-manager.c:1929 +msgid "Automatically quote original when replying" +msgstr "Tự động trích dẫn thân thư gốc khi trả lời" + +#: ../src/pref-manager.c:1932 +msgid "Don't include HTML parts as text when replying or forwarding mail" +msgstr "" +"Không bao gồm phần HTML theo dạng chữ thô khi trả lời hay gởi chuyển tiếp" + +#: ../src/pref-manager.c:1935 +msgid "Forward a mail as attachment instead of quoting it" +msgstr "Chuyển tiếp thư theo dạng đính kèm thay vì trích dẫn nó" + +#: ../src/pref-manager.c:1938 +msgid "Send button always queues outgoing mail in outbox" +msgstr "Nút Gởi luôn sắp hàng thư đi trong hộp Thư Đi" + +#: ../src/pref-manager.c:1941 +msgid "Copy outgoing messages to sentbox" +msgstr "Sao chép thư gởi đi vào hộp Đã Gởi" + +#: ../src/pref-manager.c:1958 +msgid "Status Messages" +msgstr "Thông điệp trạng thái" + +#: ../app/dia-props.c:242 ../gncal/gnomecal-prefs.c:1849 ../pan/prefs.c:1975 +#: po/silky.glade.h:75 +msgid "Colors" +msgstr "Màu sắc" + +#: ../src/mlview-validator-window.cc:702 ../pan/message-window.c:1140 +#: ../mimedir/mimedir-vcard-phone.c:238 +#, fuzzy +msgid "Message" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Thông điệp\n" +"#-#-#-#-# libmimedir.vi.po (libmimedir HEADnReport-Msgid-Bugs-To: ) #-#-#-" +"#-#\n" +"Tin nhẳn" + +#: ../src/pref-manager.c:1964 +msgid "Sort and Thread" +msgstr "Sắp xếp và Nhánh" + +#: ../src/pref-manager.c:1989 +msgid "Main Window" +msgstr "Cửa sổ chính" + +#: ../src/pref-manager.c:1992 +msgid "Use preview pane" +msgstr "Dùng khung Xem thử" + +#: ../src/pref-manager.c:1994 +msgid "Show mailbox statistics in left pane" +msgstr "Hiển thị thống kê hộp thư trong ô bên trái" + +#: ../src/pref-manager.c:1996 +msgid "Use alternative main window layout" +msgstr "Dùng bố trí cửa sổ chính xen kẽ" + +#: ../src/pref-manager.c:1998 +msgid "Automatically view message when mailbox opened" +msgstr "Tự động xem thư khi mở hộp thư" + +#: ../src/pref-manager.c:2004 +msgid "PageUp/PageDown keys scroll message by:" +msgstr "Phím PageUp/PageDown cuộn thư theo :" + +#: ../src/pref-manager.c:2015 ../libgimp/gimpunitcache.c:57 +#: ../app/core/gimpunit.c:70 ../src/orca/chnames.py:32 +#: ../src/orca/speechgenerator.py:891 +#, fuzzy +msgid "percent" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"phần trăm\n" +"#-#-#-#-# orca.vi.po (orca HEAD) #-#-#-#-#\n" +"dấu phần trăm" + +#: ../src/pref-manager.c:2030 +msgid "Display Progress Dialog" +msgstr "Hiện hộp thoại Tiến hành" + +#: ../src/pref-manager.c:2050 ../glade2/meldapp.glade.h:34 +msgid "Encoding" +msgstr "Bộ ký tự" + +#: ../src/pref-manager.c:2055 +msgid "Date encoding (for strftime):" +msgstr "Mã hoá ngày (cho strftime):" + +#: ../src/pref-manager.c:2057 +msgid "Selected headers:" +msgstr "Dòng đầu đã chọn:" + +#: ../src/pref-manager.c:2078 ../src/pref-manager.c:2083 +msgid "Information Messages" +msgstr "Thông điệp thông tin" + +#: ../src/pref-manager.c:2087 +msgid "Warning Messages" +msgstr "Thông điệp cảnh báo" + +#: ../src/pref-manager.c:2091 +msgid "Error Messages" +msgstr "Thông điệp lỗi" + +#: ../src/pref-manager.c:2095 +msgid "Fatal Error Messages" +msgstr "Thông điệp lỗi nghiêm trọng" + +#: ../src/pref-manager.c:2099 +msgid "Debug Messages" +msgstr "Thông điệp gỡ lỗi" + +#: ../src/pref-manager.c:2125 +msgid "Message Colors" +msgstr "Màu sác thông điệp" + +#: ../src/pref-manager.c:2130 +#, c-format +msgid "Quote level %d color" +msgstr "Màu cấp trích dẵn %d" + +#: ../src/pref-manager.c:2143 ../gtk/gtkaboutdialog.c:429 +msgid "Link Color" +msgstr "Màu liên kết" + +#: ../src/pref-manager.c:2145 +msgid "Hyperlink color" +msgstr "Màu siêu liên kết" + +#: ../src/pref-manager.c:2156 +msgid "Composition Window" +msgstr "Cửa sổ soạn thảo" + +#: ../src/pref-manager.c:2160 +msgid "Invalid or incomplete address label color" +msgstr "Màu nhãn địa chỉ không hợp lệ/hoàn tất" + +#: ../src/pref-manager.c:2185 ../app/pdb/internal_procs.c:129 +#: ../pan/prefs.c:1039 ../pan/prefs.c:1974 po/silky.glade.h:106 +msgid "Fonts" +msgstr "Phông chữ" + +#: ../src/pref-manager.c:2189 +msgid "Message Font" +msgstr "Phông chữ thư" + +#: ../src/pref-manager.c:2201 +msgid "Message Subject Font" +msgstr "Phông chữ chủ đề thư" + +#: ../src/pref-manager.c:2232 +msgid "Sorting and Threading" +msgstr "Sắp xếp và Nhánh" + +#: ../src/pref-manager.c:2236 +msgid "Default sort column" +msgstr "Cột sắp xếp mặc định" + +#: ../src/pref-manager.c:2240 +msgid "Default threading style" +msgstr "Kiểu nhánh mặc định" + +#: ../src/pref-manager.c:2245 +msgid "Expand threads on open" +msgstr "Bung các nhánh khi mở" + +#: ../src/pref-manager.c:2322 +msgid "Pspell Settings" +msgstr "Thiết lập Pspell" + +#: ../src/pref-manager.c:2328 +msgid "Spell Check Module" +msgstr "Mô-đun kiểm lỗi chính tả" + +#: ../src/pref-manager.c:2334 +msgid "Suggestion Level" +msgstr "Cấp gợi ý" + +#. do the ignore length +#: ../src/pref-manager.c:2339 +msgid "Ignore words shorter than" +msgstr "Bỏ qua từ ngắn hơn" + +#: ../src/pref-manager.c:2360 +msgid "Miscellaneous Spelling Settings" +msgstr "Thiết lập Chính tả Lặt vặt" + +#: ../src/pref-manager.c:2362 +msgid "Check signature" +msgstr "Kiểm tra chữ ký" + +#: ../src/pref-manager.c:2363 +msgid "Check quoted" +msgstr "Kiểm tra trích dẫn" + +#: ../src/pref-manager.c:2389 ../app/pdb/internal_procs.c:159 +msgid "Miscellaneous" +msgstr "Lặt vặt" + +#: ../src/pref-manager.c:2391 ../app/actions/actions.c:115 +#: src/gtkam-debug.c:329 +msgid "Debug" +msgstr "Gỡ lỗi" + +#: ../src/pref-manager.c:2392 +msgid "Empty Trash on exit" +msgstr "Đổ Rác khi thoát" + +#: ../src/pref-manager.c:2398 +msgid "Automatically close mailbox if unused more than" +msgstr "Tự động đóng hộp thư nếu không dùng sau" + +#: ../src/pref-manager.c:2426 +msgid "Deleting Messages" +msgstr "Xoá bỏ thư" + +#: ../src/pref-manager.c:2428 +msgid "" +"The following setting is global, but may be overridden\n" +"for the selected mailbox using Mailbox -> Hide messages:" +msgstr "" +"Thiết lập theo sau là toàn cục, nhưng có thẻ bị đè\n" +"cho hộp thư được chọn, dùng Hộp thư → Ẩn thư :" + +#: ../src/pref-manager.c:2436 +msgid "Hide messages marked as deleted" +msgstr "Ẩn thư có nhãn Đã xoá bỏ" + +#: ../src/pref-manager.c:2438 +msgid "The following settings are global." +msgstr "Thiết lập theo sau là toàn cục." + +#: ../src/pref-manager.c:2443 +msgid "Expunge deleted messages when mailbox is closed" +msgstr "Xoá hẳn các thư đã xoá bỏ khi đóng thư hợp" + +#: ../src/pref-manager.c:2450 +msgid " ...and if mailbox is unused more than" +msgstr " ..và nếu hộp thư không dùng sau" + +#: ../src/pref-manager.c:2476 +msgid "Message Window" +msgstr "Cửa sổ thư" + +#: ../src/pref-manager.c:2478 +msgid "Action after moving/trashing a message" +msgstr "Hành động sau khi chuyển/xoá bỏ thư" + +#: ../src/pref-manager.c:2513 ../gncal/gnomecal-prefs.c:1506 +#: ../data/gtkorphan.glade.h:19 ../glade/glade_project_window.c:385 +#: src/prefsdlg.cpp:52 src/prefsdlg.cpp:60 src/prefsdlg.cpp:67 +msgid "Options" +msgstr "Tùy chọn" + +#: ../src/pref-manager.c:2516 +msgid "Open Inbox upon startup" +msgstr "Mở hộp Thư Đến khi khởi chạy" + +#: ../src/pref-manager.c:2518 +msgid "Check mail upon startup" +msgstr "Kiểm tra thư mới khi khởi chạy" + +#: ../src/pref-manager.c:2520 +msgid "Remember open mailboxes between sessions" +msgstr "Nhớ các hộp thư đang mở giữa hai phiên chạy" + +#: ../src/pref-manager.c:2534 +msgid "Folder Scanning" +msgstr "Quét thư mục" + +#: ../src/pref-manager.c:2536 +msgid "" +"Choose depth 1 for fast startup; this defers scanning some folders.\n" +"To see more of the tree at startup, choose a greater depth." +msgstr "" +"Chọn độ sâu 1 để khởi chạy nhanh, điều này trì hoãn quét một số thư mục.\n" +"Để xem cụ thể cây hơn khi khởi chạy, hãy chọn độ sâu hơn." + +#: ../src/pref-manager.c:2546 +msgid "Scan local folders to depth" +msgstr "Quét thư mục cục bộ đến độ sâu" + +#: ../src/pref-manager.c:2559 +msgid "Scan IMAP folders to depth" +msgstr "Quét thư mục IMAP đến độ sâu" + +#: ../src/pref-manager.c:2624 +msgid "Address Book Name" +msgstr "Tên sổ địa chỉ" + +#: ../src/pref-manager.c:2632 +msgid "Expand aliases" +msgstr "Bung bí danh" + +#: ../src/pref-manager.c:2651 +msgid "_Set as default" +msgstr "Đặt là _mặc định" + +#: ../src/pref-manager.c:2965 +msgid "Remote POP3 mailbox..." +msgstr "Hộp thư POP3 ở xa..." + +#: ../src/pref-manager.c:3088 +msgid "Show nothing" +msgstr "Đừng hiện gì" + +#: ../src/pref-manager.c:3090 +msgid "Show dialog" +msgstr "Hiện hộp thoại" + +#: ../src/pref-manager.c:3092 +msgid "Show in list" +msgstr "Hiện trong danh sách" + +#: ../src/pref-manager.c:3094 +msgid "Show in status bar" +msgstr "Hiện lên thanh trạng thái" + +#: ../src/pref-manager.c:3096 +msgid "Print to console" +msgstr "In ra bàn giao tiếp" + +#: ../src/pref-manager.c:3107 +msgid "Ask me" +msgstr "Hỏi tôi" + +#: ../src/pref-manager.c:3184 +msgid "Show next unread message" +msgstr "Hiển thị thư chưa đọc kế tiếp" + +#: ../src/pref-manager.c:3185 +msgid "Show next message" +msgstr "Hiển thị thư kế tiếp" + +#: ../src/pref-manager.c:3186 +msgid "Close message window" +msgstr "Đóng cửa sổ thư" + +#: ../src/pref-manager.c:3210 +#, c-format +msgid "Error displaying link_id %s: %s\n" +msgstr "Gặp lỗi khi hiển thị ID liên kết %s: %s\n" + +#: ../src/print.c:246 ../src/print.c:727 ../src/print.c:66 +#, c-format +msgid "Page: %i/%i" +msgstr "Trang: %i/%i" + +#: ../src/print.c:662 +msgid "" +"Preparing an HTML part, which must start on a new page.\n" +"Print this part?" +msgstr "" +"Đang chuẩn bị một phần dạng HTML, mà phải bắt đầu trên trang mới.\n" +"In phần này không?" + +#: ../gnome/applet/wireless-applet.glade.h:39 Expense/expense.c:1700 +msgid "Type:" +msgstr "Kiểu :" + +#: ../src/print.c:984 ../widgets/misc/e-attachment.glade.h:4 +#: ../data/glade/song-info.glade.h:8 +msgid "File name:" +msgstr "Tên tập tin:" + +#: ../src/print.c:1300 +#, c-format +msgid "This is an inline %s signed %s message part:" +msgstr "Đây là phần thư trực tiếp %s có chữ ký %s:" + +#: ../src/print.c:1547 +msgid "Font available for printing" +msgstr "Phông chữ có sẵn để in" + +#: ../src/print.c:1553 +#, c-format +msgid "Font not available for printing. Closest: %s" +msgstr "Phông chữ không có sẵn để in. Gần nhất: %s" + +#: ../glade/gbwidgets/gbfontselectiondialog.c:70 ../pan/pan-font-button.c:55 +msgid "Select Font" +msgstr "Chọn phông chữ" + +#: ../src/print.c:1599 ../src/planner-task-dialog.c:2383 +msgid "Change..." +msgstr "Đổi..." + +#: ../src/print.c:1634 +msgid "Print message" +msgstr "In thư" + +#: ../src/print.c:1645 ../app/actions/dialogs-actions.c:150 +msgid "_Fonts" +msgstr "_Phông chữ" + +#: ../src/print.c:1648 ../src/preferences.c:291 +msgid "Header font" +msgstr "Phông chữ đầu trang" + +#: ../src/print.c:1650 +msgid "Body font" +msgstr "Phông chữ thân" + +#: ../src/print.c:1652 +msgid "Footer font" +msgstr "Phông chữ chân trang" + +#. highlight cited stuff +#: ../src/print.c:1656 +msgid "Highlight cited text" +msgstr "Tô sáng trích dẫn" + +#: ../src/print.c:1660 +msgid "_Enable highlighting of cited text" +msgstr "Bật tô _sáng trích dẫn" + +#: ../src/print.c:1686 +#, c-format +msgid "" +"Balsa could not find font \"%s\".\n" +"Use the \"Fonts\" page on the \"Print message\" dialog to change it." +msgstr "" +"Balsa không tìm thấy phông chữ « %s ».\n" +"Hãy dùng trang « Phông chữ » trong hộp thoại « In thư » để thay đổi." + +#: ../src/print.c:1787 +msgid "Balsa: message print preview" +msgstr "Balsa: xem thử bản in thư" + +#: ../src/save-restore.c:613 +msgid "Error during filters loading: " +msgstr "Gặp lỗi trong khi tải các bộ lọc: " + +#: ../src/save-restore.c:615 +#, c-format +msgid "" +"Error during filters loading: %s\n" +"Filters may not be correct." +msgstr "" +"Gặp lỗi trong khi tải các bộ lọc: %s\n" +"Có thể bộ lọc không đúng." + +#: ../src/save-restore.c:730 +msgid "The option not to recognize \"format=flowed\" text has been removed." +msgstr "" +"Tùy chọn để không nhận ra văn bản « dạng thức=trôi chảy » đã được gỡ bỏ." + +#: ../src/save-restore.c:991 +msgid "" +"The option not to send \"format=flowed\" is now on the Options menu of the " +"compose window." +msgstr "" +"Tùy chọn để không gởi văn bản « dạng thức=trôi chảy » hiện nằm trong trình " +"đơn các Tùy Chọn của cửa sổ soạn thảo." + +#: ../src/save-restore.c:1023 +msgid "" +"The option to request a MDN is now on the Options menu of the compose window." +msgstr "" +"Tùy chọn để yêu cầu MDN hiện nằm trong trình đơn các Tùy Chọn của cửa sổ " +"soạn thảo." + +#: ../src/save-restore.c:2042 +msgid "Error opening GConf database\n" +msgstr "Gặp lỗi khi mở cơ sở dữ liệu GConf.\n" + +#: ../src/save-restore.c:2050 ../src/save-restore.c:2061 +#, c-format +msgid "Error setting GConf field: %s\n" +msgstr "Gặp lỗi khi thiết lập trường GConf: %s\n" + +#: ../src/sendmsg-window.c:234 +msgid "_Brazilian" +msgstr "Bồ-đào-nha (_Bra-xin)" + +#: ../src/sendmsg-window.c:235 +msgid "_Catalan" +msgstr "_Ca-ta-lan" + +#: ../src/sendmsg-window.c:236 +msgid "_Chinese Simplified" +msgstr "_Hoa phổ thông" + +#: ../src/sendmsg-window.c:237 +msgid "_Chinese Traditional" +msgstr "_Hoa truyền thống" + +#: ../src/sendmsg-window.c:238 +msgid "_Czech" +msgstr "_Séc" + +#: ../src/sendmsg-window.c:239 +msgid "_Danish" +msgstr "_Đan-mạch" + +#: ../src/sendmsg-window.c:240 +msgid "_Dutch" +msgstr "_Hoà-lan" + +#: ../src/sendmsg-window.c:241 +msgid "_English (American)" +msgstr "Anh (_Mỹ)" + +#: ../src/sendmsg-window.c:242 +msgid "_English (British)" +msgstr "Anh (_Quốc Anh)" + +#: ../src/sendmsg-window.c:243 +msgid "_Esperanto" +msgstr "_Etpêrantô" + +#: ../src/sendmsg-window.c:244 +msgid "_Estonian" +msgstr "_Et-tô-ni-a" + +#: ../src/sendmsg-window.c:245 +msgid "_Finnish" +msgstr "_Phần-lan" + +#: ../src/sendmsg-window.c:246 +msgid "_French" +msgstr "_Pháp" + +#: ../src/sendmsg-window.c:247 +msgid "_German" +msgstr "_Đức" + +#: ../src/sendmsg-window.c:248 +msgid "_Greek" +msgstr "_Hy-lạp" + +#: ../src/sendmsg-window.c:249 +msgid "_Hebrew" +msgstr "_Do-thái" + +#: ../src/sendmsg-window.c:250 +msgid "_Hungarian" +msgstr "_Hung-gia-lợi" + +#: ../src/sendmsg-window.c:251 +msgid "_Italian" +msgstr "_Ý" + +#: ../src/sendmsg-window.c:252 +msgid "_Japanese (JIS)" +msgstr "Nhật Bản (_JIS)" + +#: ../src/sendmsg-window.c:253 +msgid "_Korean" +msgstr "_Triều tiên" + +#: ../src/sendmsg-window.c:254 +msgid "_Latvian" +msgstr "_Lát-vi-a" + +#: ../src/sendmsg-window.c:255 +msgid "_Lithuanian" +msgstr "_Li-tu-a-ni" + +#: ../src/sendmsg-window.c:256 +msgid "_Norwegian" +msgstr "_Na-uy" + +#: ../src/sendmsg-window.c:257 +msgid "_Polish" +msgstr "_Ba Lan" + +#: ../src/sendmsg-window.c:258 +msgid "_Portugese" +msgstr "_Bồ-đào-nha" + +#: ../src/sendmsg-window.c:259 +msgid "_Romanian" +msgstr "_Lỗ-má-ni" + +#: ../src/sendmsg-window.c:260 +msgid "_Russian (ISO)" +msgstr "_Nga" + +#: ../src/sendmsg-window.c:261 +msgid "_Russian (KOI)" +msgstr "Nga (_KOI)" + +#: ../src/sendmsg-window.c:262 +msgid "_Serbian" +msgstr "_Xéc-bi" + +#: ../src/sendmsg-window.c:263 +msgid "_Serbian (Latin)" +msgstr "_Xéc-bi (La-tinh)" + +#: ../src/sendmsg-window.c:264 +msgid "_Slovak" +msgstr "_Xlô-vác" + +#: ../src/sendmsg-window.c:265 +msgid "_Spanish" +msgstr "_Tây-ban-nha" + +#: ../src/sendmsg-window.c:266 +msgid "_Swedish" +msgstr "_Thuỵ-điển" + +#: ../src/sendmsg-window.c:267 +msgid "_Turkish" +msgstr "_Thổ-nhĩ-kỳ" + +#: ../src/sendmsg-window.c:268 +msgid "_Ukrainian" +msgstr "_U-cợ-rainh" + +#: ../src/sendmsg-window.c:269 +msgid "_Generic UTF-8" +msgstr "_UTF-8 chung" + +#: ../src/sendmsg-window.c:293 +msgid "_GnuPG uses MIME mode" +msgstr "_GnuPG dùng chế độ MIME" + +#: ../src/sendmsg-window.c:299 +msgid "_GnuPG uses old OpenPGP mode" +msgstr "_GnuPG dùng chế độ OpenPGP" + +#: ../src/sendmsg-window.c:306 +msgid "_S/MIME mode (GpgSM)" +msgstr "Chế độ _S/MIME (GpgSM)" + +#: ../src/sendmsg-window.c:321 ../src/sendmsg-window.c:499 +msgid "_Include File..." +msgstr "_Gồm tập tin..." + +#: ../src/sendmsg-window.c:324 ../src/sendmsg-window.c:512 +msgid "_Attach File..." +msgstr "Đính _kèm tập tin..." + +#: ../src/sendmsg-window.c:327 ../src/sendmsg-window.c:501 +msgid "I_nclude Message(s)" +msgstr "Gồm (các) th_ư" + +#: ../src/sendmsg-window.c:330 ../src/sendmsg-window.c:504 +msgid "Attach _Message(s)" +msgstr "Đính kè_m (các) thư" + +#: ../src/sendmsg-window.c:336 ../src/sendmsg-window.c:529 +msgid "Sen_d" +msgstr "_Gởi" + +#: ../src/toolbar-factory.c:111 ../ui/evolution-message-composer.xml.h:30 +#, fuzzy +msgid "Send this message" +msgstr "" +"#-#-#-#-# balsa.po (balsa HEAD) #-#-#-#-#\n" +"Gởi thư này\n" +"#-#-#-#-# Compendium03.po (apt) #-#-#-#-#\n" +"Gởi thông điệp này" + +#: ../src/sendmsg-window.c:341 ../data/glade/AddWindow.glade.h:2 +#, fuzzy +msgid "_Queue" +msgstr "" +"#-#-#-#-# balsa.po (balsa HEAD) #-#-#-#-#\n" +"_Sắp hàng\n" +"#-#-#-#-# Compendium03.po (apt) #-#-#-#-#\n" +"_Hàng đợi" + +#: ../src/sendmsg-window.c:342 ../src/sendmsg-window.c:535 +msgid "Queue this message in Outbox for sending" +msgstr "Sắp hàng thư này trong hộp Thư Đi để gởi" + +#: ../src/sendmsg-window.c:346 +msgid "_Postpone" +msgstr "_Hoãn" + +#: ../src/sendmsg-window.c:349 ../src/sendmsg-window.c:516 +#: ../gtk/gtkstock.c:400 ../app/actions/file-actions.c:86 +msgid "_Save" +msgstr "_Lưu" + +#: ../src/sendmsg-window.c:350 ../src/sendmsg-window.c:517 +msgid "Save this message" +msgstr "Lưu thư này" + +#: ../src/sendmsg-window.c:354 ../src/sendmsg-window.c:521 +msgid "Print the edited message" +msgstr "In thư đã soạn thảo" + +#: ../src/sendmsg-window.c:383 ../src/sendmsg-window.c:546 +msgid "_Wrap Body" +msgstr "_Cuộn thân" + +#: ../src/sendmsg-window.c:387 ../src/sendmsg-window.c:550 +msgid "_Reflow Selected Text" +msgstr "Cuộn _lại phần đã chọn" + +#: ../src/sendmsg-window.c:392 ../src/sendmsg-window.c:578 +msgid "Insert Si_gnature" +msgstr "Chèn chữ _ký" + +#: ../src/sendmsg-window.c:396 ../src/sendmsg-window.c:554 +msgid "_Quote Message(s)" +msgstr "Trích _dẫn (các) thư" + +#: ../src/sendmsg-window.c:402 +msgid "C_heck spelling" +msgstr "_Kiểm tra chính tả" + +#: ../src/sendmsg-window.c:403 ../src/sendmsg-window.c:407 +#: ../src/sendmsg-window.c:632 +msgid "Check the spelling of the message" +msgstr "Kiểm tra chính tả thư là đúng" + +#: ../src/sendmsg-window.c:406 ../src/sendmsg-window.c:630 +msgid "C_heck Spelling" +msgstr "_Kiểm tra chính tả" + +#: ../src/sendmsg-window.c:413 ../src/sendmsg-window.c:638 +msgid "Select _Identity..." +msgstr "Chọn _thực thể..." + +#: ../src/sendmsg-window.c:414 ../src/sendmsg-window.c:639 +msgid "Select the Identity to use for the message" +msgstr "Chọn thực thể cần dùng cho thư này" + +#: ../src/sendmsg-window.c:419 +msgid "_Edit with Gnome-Editor" +msgstr "Sửa đổi trong Bộ hiệu chỉnh Gnome" + +#: ../src/sendmsg-window.c:420 +msgid "Edit the current message with the default Gnome editor" +msgstr "Sửa đổi thư hiện thời bằng bộ hiệu chỉnh Gnome mặc định" + +#: ../src/sendmsg-window.c:432 ../src/sendmsg-window.c:591 +msgid "Fr_om" +msgstr "_Từ" + +#: ../src/sendmsg-window.c:434 ../src/sendmsg-window.c:593 +msgid "_Cc" +msgstr "_Cc" + +#: ../src/sendmsg-window.c:436 ../src/sendmsg-window.c:595 +msgid "_Bcc" +msgstr "_Bcc" + +#: ../src/sendmsg-window.c:438 ../src/sendmsg-window.c:597 +msgid "_Fcc" +msgstr "_Bcc" + +#: ../src/sendmsg-window.c:440 +msgid "_Reply To" +msgstr "T_rả lời" + +#: ../src/sendmsg-window.c:446 ../src/sendmsg-window.c:643 +msgid "_Request Disposition Notification" +msgstr "_Yêu cầu thông báo cách chuyển nhượng thư" + +#: ../src/sendmsg-window.c:449 ../src/sendmsg-window.c:609 +msgid "_Format = Flowed" +msgstr "_Dạng thức=trôi chảy" + +#: ../src/sendmsg-window.c:454 ../src/sendmsg-window.c:614 +msgid "_Sign Message" +msgstr "_Ký tên thư" + +#: ../src/sendmsg-window.c:455 ../src/sendmsg-window.c:615 +msgid "signs the message using GnuPG" +msgstr "ký tên thư bằng GnuPG" + +#: ../src/sendmsg-window.c:458 ../src/sendmsg-window.c:618 +msgid "_Encrypt Message" +msgstr "_Mật mã hóa thư" + +#: ../src/sendmsg-window.c:459 ../src/sendmsg-window.c:619 +msgid "signs the message using GnuPG for all To: and CC: recipients" +msgstr "" +"mật mã hóa thư bằng GnuPG cho mọi người nhận kiểu Cho (To:) và Chép Cho (Cc:)" + +#: ../src/sendmsg-window.c:479 ../src/sendmsg-window.c:659 +#: ../libnautilus-private/nautilus-column-chooser.c:413 +#: ../data/glade/column-dialog.glade.h:5 +#: ../bonobo/bonobo-ui-config-widget.c:275 +msgid "_Show" +msgstr "_Hiện" + +#: ../src/sendmsg-window.c:481 ../src/sendmsg-window.c:636 +msgid "_Language" +msgstr "_Ngôn ngữ" + +#: ../src/sendmsg-window.c:483 ../ui/evolution-editor.xml.h:21 +#: ../dwell-selection.xml.in.h:14 +msgid "_Options" +msgstr "Tù_y chọn" + +#: ../src/sendmsg-window.c:525 +msgid "Sa_ve and Close" +msgstr "_Lưu và Đóng" + +#: ../src/sendmsg-window.c:534 +msgid "Send _Later" +msgstr "Lưu _sau này" + +#: ../src/sendmsg-window.c:628 +msgid "Toggle Spell C_hecker" +msgstr "Bật/tắt bộ _kiểm tra lỗi chính tả" + +#: ../src/sendmsg-window.c:689 ../mail/mail-config.glade.h:45 +#: ../mail/message-list.etspec.h:1 +msgid "Attachment" +msgstr "Đính kèm" + +#: ../src/sendmsg-window.c:689 ../mail/mail-config.glade.h:91 +#: ../mail/mail-config.glade.h:92 +msgid "Inline" +msgstr "Trực tiếp" + +#: ../Pyblio/GnomeUI/Fields.py:42 +msgid "Reference" +msgstr "Tham chiếu" + +#: ../src/sendmsg-window.c:917 +#, c-format +msgid "" +"The message to '%s' is modified.\n" +"Save message to Draftbox?" +msgstr "" +"Thư gởi đến « %s » bị thay đổi.\n" +"Có lưu thư vào hộp thư Nháp không?" + +#: ../src/sendmsg-window.c:942 +#, c-format +msgid "" +"The message to '%s' was saved in Draftbox.\n" +"Remove message from Draftbox?" +msgstr "" +"Thư gởi đến « %s » đã được lưu vào hộp thư Nháp.\n" +"Có gỡ bỏ thư ra hộp thư Nháp không?" + +#: ../src/sendmsg-window.c:1247 +msgid "Gnome editor is not defined in your preferred applications." +msgstr "Chưa ghi rõ bộ hiệu chỉnh Gnome trong các ứng dụng ưa thích của bạn." + +#: ../src/sendmsg-window.c:1298 +msgid "Select Identity" +msgstr "Chọn thực thể" + +#: ../src/sendmsg-window.c:1677 +#, c-format +msgid "" +"Saying yes will not send the file `%s' itself, but just a MIME message/" +"external-body reference. Note that the recipient must have proper " +"permissions to see the `real' file.\n" +"\n" +"Do you really want to attach this file as reference?" +msgstr "" +"Trả lời Có sẽ không gởi tập tin « %s » chính nó, nhưng là gởi thông điệp " +"MIME / tham chiếu phần thân ngoài. Lưu ý là người nhận phải có quyền truy " +"cập đúng để xem tập tin « thật ».\n" +"\n" +"Bạn có muốn đính kèm tập tin này dạng tham chiếu không?" + +#: ../src/sendmsg-window.c:1688 +msgid "Attach as Reference?" +msgstr "Đính kèm dạng tham chiếu?" + +#: ../src/sendmsg-window.c:1801 +msgid "Choose charset" +msgstr "Chọn bộ ký tự" + +#: ../src/sendmsg-window.c:1808 +#, c-format +msgid "" +"File\n" +"%s\n" +"is not encoded in US-ASCII or UTF-8.\n" +"Please choose the charset used to encode the file." +msgstr "" +"Tập tin\n" +"%s\n" +"chưa được mã hóa theo US-ASCII hay UTF-8.\n" +"Hãy chọn bộ ký tự dùng để mã hóa tập tin." + +#: ../src/sendmsg-window.c:1824 +msgid "Attach as MIME type:" +msgstr "Đínhy kèm dạng kiểu MIME:" + +#: ../src/sendmsg-window.c:1880 +#, c-format +msgid "Character set for file %s changed from \"%s\" to \"%s\"." +msgstr "Bộ ký tự cho tập tin %s đã thay đổi từ « %s » sang « %s »." + +#: ../src/sendmsg-window.c:1923 ../src/sendmsg-window.c:5072 +msgid "(no subject)" +msgstr "(không có chủ đề)" + +#: ../src/sendmsg-window.c:2009 +msgid "forwarded message" +msgstr "thư đã chuyển tiếp" + +#: ../src/sendmsg-window.c:2014 +#, c-format +msgid "Message from %s, subject: \"%s\"" +msgstr "Thừ từ %s, chủ đề: « %s »" + +#: ../src/sendmsg-window.c:2032 +#, c-format +msgid "Error converting \"%s\" to UTF-8: %s\n" +msgstr "Gặp lỗi khi chuyển đổi « %s » sang UTF-8: %s\n" + +#: ../Pyblio/GnomeUI/Config.py:435 ../Pyblio/GnomeUI/Config.py:549 +#: ../storage/sunone-permissions-dialog.glade.h:27 install_gui.c:324 +#: app/sample-editor.c:455 +msgid "Remove" +msgstr "Gỡ bỏ" + +#: ../src/sendmsg-window.c:2185 src/menus.c:356 ../list-ui.c:541 +#: ../src/glade-project-window.c:309 +msgid "Open..." +msgstr "Mở..." + +#: ../src/sendmsg-window.c:2197 +msgid "(URL)" +msgstr "(Địa chỉ Mạng)" + +#: ../src/sendmsg-window.c:2218 +#, c-format +msgid "Cannot get info on file '%s': %s" +msgstr "Không thể lấy thông tin về tập tin « %s »: %s" + +#: ../src/sendmsg-window.c:2223 +#, c-format +msgid "Attachment %s is not a regular file." +msgstr "Đính kèm %s không phải là tập tin chuẩn." + +#: ../src/sendmsg-window.c:2226 +#, c-format +msgid "File %s cannot be read\n" +msgstr "Tập tin %s không có khả năng đọc.\n" + +#: ../src/sendmsg-window.c:2275 +msgid "Attach file" +msgstr "Đính kèm tập tin" + +#: ../src/sendmsg-window.c:2361 ../src/sendmsg-window.c:2470 +#: ../src/sendmsg-window.c:4287 +msgid "" +"Attaching message failed.\n" +"Possible reason: not enough temporary space" +msgstr "" +"Việc đính kèm thư bị lỗi.\n" +"Lý do có thể: không đủ chỗ tạm thời" + +#: ../src/sendmsg-window.c:2690 +msgid "F_rom:" +msgstr "_Từ :" + +#: ../src/sendmsg-window.c:2825 ../plug-ins/common/mail.c:605 +msgid "S_ubject:" +msgstr "C_hủ đề:" + +#. fcc: mailbox folder where the message copy will be written to +#: ../src/sendmsg-window.c:2839 +msgid "F_cc:" +msgstr "F_cc:" + +#. Reply To: +#: ../src/sendmsg-window.c:2870 +msgid "_Reply To:" +msgstr "T_rả lời:" + +#. Attachment list +#: ../src/sendmsg-window.c:2875 +msgid "_Attachments:" +msgstr "Đính _kèm:" + +#: ../src/sendmsg-window.c:2920 ../plug-ins/common/waves.c:273 +msgid "Mode" +msgstr "Chế độ" + +#: ../pan/task-manager.c:756 src/dictmanagedlg.cpp:519 +#: ../storage/sunone-subscription-dialog.c:488 +#: ../mimedir/mimedir-vcomponent.c:276 schroot/sbuild-chroot.cc:387 +#: app/audioconfig.c:263 +msgid "Description" +msgstr "Mô tả" + +#: ../src/sendmsg-window.c:3253 +#, c-format +msgid "Could not save attachment: %s" +msgstr "Không thể lưu đính kèm: %s" + +#: ../src/sendmsg-window.c:3289 +msgid "you" +msgstr "bạn" + +#: ../src/sendmsg-window.c:3298 +#, c-format +msgid "------forwarded message from %s------\n" +msgstr "━━━thư đã chuyển tiếp từ %s━━━\n" + +#: ../src/sendmsg-window.c:3337 +#, c-format +msgid "Message-ID: %s\n" +msgstr "ID thư : %s\n" + +#: ../src/sendmsg-window.c:3343 +msgid "References:" +msgstr "Tham chiếu :" + +#: ../src/sendmsg-window.c:3354 +#, c-format +msgid "On %s, %s wrote:\n" +msgstr "Vào %s, %s đã viết:\n" + +#: ../src/sendmsg-window.c:3356 +#, c-format +msgid "%s wrote:\n" +msgstr "%s đã viết:\n" + +#: ../src/sendmsg-window.c:3459 +msgid "No signature found!" +msgstr "• Không tìm thấy chữ ký. •" + +#: ../src/sendmsg-window.c:3629 +msgid "Could not save message." +msgstr "Không thể lưu thư." + +#: ../src/sendmsg-window.c:3636 +#, c-format +msgid "Could not open draftbox: %s" +msgstr "Không thể mở hộp thư Nháp: %s" + +#: ../src/sendmsg-window.c:3657 +msgid "Message saved." +msgstr "Thư đã được lưu." + +#: ../src/sendmsg-window.c:4444 +#, c-format +msgid "Error executing signature generator %s" +msgstr "Gặp lỗi khi thực hiện bộ tạo ra chữ ký %s" + +#: ../src/sendmsg-window.c:4456 +#, c-format +msgid "Cannot open signature file '%s' for reading" +msgstr "Không thể mở tập tin chữ ký « %s » để đọc." + +#: ../src/sendmsg-window.c:4466 +#, c-format +msgid "Error reading signature from %s" +msgstr "Gặp lỗi khi đọc chữ ký từ %s." + +#: ../src/sendmsg-window.c:4470 +#, c-format +msgid "Signature in %s is not a UTF-8 text." +msgstr "Chữ ký trong %s không phải là chuỗi UTF-8." + +#: ../src/sendmsg-window.c:4533 +#, c-format +msgid "Could not open the file %s.\n" +msgstr "Không thể mở tập tin %s.\n" + +#: ../src/sendmsg-window.c:4589 +msgid "Include file" +msgstr "Gồm tập tin" + +#. Translators: please do not translate Face. +#: ../src/sendmsg-window.c:4804 +#, c-format +msgid "Could not load Face header file %s: %s" +msgstr "Không thể mở tập tin phần đầu Face %s: %s" + +#. Translators: please do not translate Face. +#: ../src/sendmsg-window.c:4807 +#, c-format +msgid "Could not load X-Face header file %s: %s" +msgstr "Không thể tải tập tin phần đầu Face %s: %s" + +#: ../src/sendmsg-window.c:4914 +msgid "Message contains national (8-bit) characters" +msgstr "Thư chứa ký tự thuộc quốc gia (8-bit)." + +#: ../src/sendmsg-window.c:4918 +msgid "" +"Balsa will encode the message in UTF-8.\n" +"Cancel the operation to choose a different language." +msgstr "" +"Balsa sẽ mã hóa thư bằng UTF-8.\n" +"Thôi thao tác này để chọn ngôn ngữ khác." + +#: ../src/sendmsg-window.c:4924 +msgid "" +"Message contains national (8-bit) characters. Balsa will " +"encode the message in UTF-8.\n" +"Cancel the operation to choose a different language." +msgstr "" +"Thư chứa ký tự thuộc quốc gia (8-bit). Balsa sẽ mã hóa thư " +"bằng UTF-8.\n" +"Thôi thao tác này để chọn ngôn ngữ khác." + +#: ../src/sendmsg-window.c:5056 +msgid "You did not specify a subject for this message" +msgstr "Chưa ghi rõ chủ đề cho thư này." + +#: ../src/sendmsg-window.c:5057 +msgid "If you would like to provide one, enter it below." +msgstr "Hãy gõ bên dưới." + +#: ../ui/message.glade.h:6 +msgid "_Send" +msgstr "_Gởi" + +#: ../src/sendmsg-window.c:5155 +msgid "" +"You selected OpenPGP mode for a message with attachments. In this mode, only " +"the first part will be signed and/or encrypted. You should select MIME mode " +"if the complete message shall be protected. Do you really want to proceed?" +msgstr "" +"Bạn đã chọn chế độ OpenPGP cho một thư có đính kèm. Trong chế độ này, chỉ " +"phần đầu tiên sẽ được ký tên và/hay mật mã. Bạn nên chọn chế độ MIME để bảo " +"vệ toàn bộ thư. Bạn thật sự muốn tiếp tục không?" + +#: ../src/sendmsg-window.c:5171 +#, c-format +msgid "sending message with gpg mode %d" +msgstr "đang gởi thư với chế độ GPG %d..." + +#: ../src/sendmsg-window.c:5208 +msgid "Message could not be created" +msgstr "Không thể tạo thư." + +#: ../src/sendmsg-window.c:5210 +msgid "Message could not be queued in outbox" +msgstr "Không thể sắp hàng thư trong hộp Thư Đi." + +#: ../src/sendmsg-window.c:5212 +msgid "Message could not be saved in sentbox" +msgstr "Không thể lưu thư trong hộp Đã Gởi." + +#: ../src/sendmsg-window.c:5214 +msgid "Message could not be sent" +msgstr "Không thể gởi thư." + +#: ../src/sendmsg-window.c:5218 +#, c-format +msgid "Send failed: %s" +msgstr "Việc gởi bị lỗi: %s" + +#: ../src/sendmsg-window.c:5301 ../src/sendmsg-window.c:5320 +msgid "Could not postpone message." +msgstr "Không thể hoãn thư." + +#: ../src/sendmsg-window.c:5315 +msgid "Message postponed." +msgstr "Thư đã được hoãn." + +#: ../src/sendmsg-window.c:5470 +#, c-format +msgid "Error starting spell checker: %s" +msgstr "Gặp lỗi khi khởi chạy bộ kiểm tra chính tả: %s" + +#: ../src/sendmsg-window.c:5635 +#, c-format +msgid "Could not compile %s" +msgstr "Không thể biên dịch %s." + +#: ../src/sendmsg-window.c:6182 +#, c-format +msgid "Reply to %s: %s" +msgstr "Trả lời %s: %s" + +#: ../src/sendmsg-window.c:6187 +#, c-format +msgid "Forward message to %s: %s" +msgstr "Chuyển tiếp thư tới %s: %s" + +#: ../src/sendmsg-window.c:6191 +#, c-format +msgid "Continue message to %s: %s" +msgstr "Tiếp tục thư cho %s: %s" + +#: ../src/sendmsg-window.c:6195 +#, c-format +msgid "New message to %s: %s" +msgstr "Thư mới cho %s: %s" + +#: ../src/spell-check.c:511 +msgid "Replace the current word with the selected suggestion" +msgstr "Thay thế từ hiện thời bằng từ đệ nghị được chọn." + +#: ../src/spell-check.c:519 +msgid "Replace all occurences of the current word with the selected suggestion" +msgstr "Thay thế mọi lần gặp từ hiện thời bằng từ đệ nghị được chọn." + +#: ../src/spell-check.c:531 +msgid "Skip the current word" +msgstr "Nhảy qua từ hiện thời" + +#: ../src/spell-check.c:537 +msgid "Skip all occurrences of the current word" +msgstr "Nhảy qua mọi lần gặp từ hiện thời" + +#: ../src/spell-check.c:547 +msgid "Add the current word to your personal dictionary" +msgstr "Thêm từ hiện thời vào từ điển cá nhân của bạn." + +#: ../src/spell-check.c:556 +msgid "Finish spell checking" +msgstr "Kết thúc kiểm tra chính tả" + +#: ../src/spell-check.c:561 +msgid "Revert all changes and finish spell checking" +msgstr "Hoàn lại mọi thay đổi và kết thúc kiểm tra chính tả" + +#: ../src/spell-check.c:592 +msgid "Spell check" +msgstr "Kiểm tra chính tả" + +#: ../src/store-address.c:104 +msgid "Store address: no addresses" +msgstr "Lưu địa chỉ: không có địa chỉ" + +#: ../src/store-address.c:181 +msgid "Store Address" +msgstr "Lưu địa chỉ" + +#: ../src/store-address.c:199 +msgid "Save this address and close the dialog?" +msgstr "Lưu địa chỉ này và đóng hộp thoại không?" + +#: ../src/store-address.c:217 +msgid "No address book selected...." +msgstr "Chưa chọn sổ địa chỉ..." + +#: ../src/store-address.c:230 +msgid "Address could not be written to this address book." +msgstr "Không thể ghi địa chỉ vào sổ địa chỉ." + +#: ../src/store-address.c:233 +msgid "Address book could not be accessed." +msgstr "Không thể truy cập sổ địa chỉ." + +#: ../src/store-address.c:235 +msgid "This mail address is already in this address book." +msgstr "Địa chỉ thư này đã có trong sổ địa chỉ này." + +#: ../src/store-address.c:238 +msgid "Unexpected address book error. Report it." +msgstr "Gặp lỗi sổ địa chỉ bất ngờ: hãy thông báo." + +#: ../src/store-address.c:254 +msgid "Choose Address Book" +msgstr "Chọn sổ địa chỉ" + +#: ../src/store-address.c:293 +msgid "Choose Address" +msgstr "Chọn địa chỉ" + +#: ../src/toolbar-factory.c:77 utils/gul-tbi-separator.c:133 +#: ../src/glade-gtk.c:2368 ../src/orca/rolenames.py:398 +#, fuzzy +msgid "Separator" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Bộ ngăn cách\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Bộ ngăn cách\n" +"#-#-#-#-# orca.vi.po (orca HEAD) #-#-#-#-#\n" +"Bộ phân cách" + +#: ../glade/glade_menu_editor.c:2412 ../glade/glade_menu_editor.c:2552 +#: ../src/glade-gtk.c:2362 Expense/expense.c:609 Expense/expense.c:1401 +#, fuzzy +msgid "Check" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Kiểm tra\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Kiểm tra\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"Séc" + +#: ../src/toolbar-factory.c:79 +msgid "Check for new email" +msgstr "Kiểm tra tìm thư mới" + +#: ../plug-ins/common/compose.c:1419 +msgid "Compose" +msgstr "Soạn thảo" + +#: ../src/toolbar-factory.c:81 +msgid "Compose message" +msgstr "Soạn thảo thư" + +#: ../src/toolbar-factory.c:82 ../app/dialogs/user-install-dialog.c:618 +#: ../gnomecard/cardlist-widget.c:1055 +msgid "Continue" +msgstr "Tiếp tục" + +#: ../src/toolbar-factory.c:83 +msgid "Continue message" +msgstr "Tiếp tục thư" + +#: ../src/toolbar-factory.c:84 ../mail/message-tag-followup.c:82 +#: ../ui/evolution-mail-message.xml.h:81 ../mail/message-tag-followup.c:81 +#: ../ui/evolution-mail-message.xml.h:79 +msgid "Reply" +msgstr "Trả lời" + +#: ../src/toolbar-factory.c:86 +msgid "" +"Reply\n" +"to all" +msgstr "" +"Trả lời\n" +"tất cả" + +#: ../src/toolbar-factory.c:87 +msgid "Reply to all recipients" +msgstr "Trả lời mọi người nhận" + +#: ../src/toolbar-factory.c:88 +msgid "" +"Reply\n" +"to group" +msgstr "" +"Trả lời\n" +"nhóm" + +#: ../src/toolbar-factory.c:90 ../libgimpbase/gimpbaseenums.c:676 +msgid "Forward" +msgstr "Chuyển tiếp" + +#: ../src/toolbar-factory.c:92 ../ui/evolution-calendar.xml.h:18 +#: ../ui/evolution-mail-message.xml.h:75 ../src/f-spot.glade.h:113 +msgid "Previous" +msgstr "Trước" + +#: ../src/toolbar-factory.c:93 +msgid "Open previous" +msgstr "Mở trước" + +#: ../src/toolbar-factory.c:94 ../src/menus.c:302 info/session.c:860 +#: makeinfo/node.c:1424 ../Pyblio/GnomeUI/Editor.py:608 +msgid "Next" +msgstr "Kế" + +#: ../src/toolbar-factory.c:95 +msgid "Open next" +msgstr "Mở kế" + +#: ../src/toolbar-factory.c:96 +msgid "" +"Next\n" +"unread" +msgstr "" +"Chưa\n" +"đọc kế" + +#: ../src/toolbar-factory.c:97 +msgid "Open next unread message" +msgstr "Mở thư chưa đọc kế tiếp." + +#: ../src/toolbar-factory.c:98 +msgid "" +"Next\n" +"flagged" +msgstr "" +"Đã đặt\n" +"cờ kế" + +#: ../src/toolbar-factory.c:99 +msgid "Open next flagged message" +msgstr "Mở thư đã đặt cờ kế tiếp." + +#: ../src/toolbar-factory.c:100 +msgid "" +"Previous\n" +"part" +msgstr "" +"Phần\n" +"trước" + +#: ../src/toolbar-factory.c:101 +msgid "View previous part of message" +msgstr "Xem phần thư trước đó." + +#: ../src/toolbar-factory.c:102 +msgid "" +"Next\n" +"part" +msgstr "" +"Phần\n" +"kế" + +#: ../src/toolbar-factory.c:103 +msgid "View next part of message" +msgstr "Xem phần thư kế tiếp." + +#: ../src/toolbar-factory.c:104 +msgid "" +"Trash /\n" +"Delete" +msgstr "" +"Rác\n" +"Xoá bỏ" + +#: ../src/toolbar-factory.c:105 +msgid "Move the current message to trash" +msgstr "Chuyển thư hiện thời vào Rác." + +#: ../src/toolbar-factory.c:106 +msgid "Postpone" +msgstr "Hoãn" + +#: ../src/toolbar-factory.c:107 +msgid "Postpone current message" +msgstr "Hoãn thư hiện thời." + +#: ../src/toolbar-factory.c:108 ../libtomboy/gedit-print.c:144 +#: ../Tomboy/Plugins/PrintNotes.cs:15 src/mainwin.cpp:1115 jpilot.c:450 +#: monthview_gui.c:517 print_gui.c:332 weekview_gui.c:343 +msgid "Print" +msgstr "In" + +#: ../tests/gnetwork-demo.c:251 po/silky.glade.h:174 +msgid "Send" +msgstr "Gởi" + +#: ../storage/GNOME_Evolution_Exchange_Storage.server.in.in.h:5 +msgid "Exchange" +msgstr "Trao đổi" + +#: ../objects/FS/function.c:822 +msgid "Attach" +msgstr "Đính kèm" + +#: ../src/toolbar-factory.c:115 +msgid "Add attachments to this message" +msgstr "Thêm đính kèm vào thư này." + +#: ../src/toolbar-factory.c:116 ../src/menus.c:263 +#: ../glade/glade_project_window.c:379 ../src/mlview-xml-document.cc:3478 +#: ../widgets/gtk+.xml.in.h:156 +msgid "Save" +msgstr "Lưu" + +#: ../src/toolbar-factory.c:117 +msgid "Save the current item" +msgstr "Lưu mục hiện thời." + +#: ../src/toolbar-factory.c:118 ../gnomecard/card-editor.c:427 +#: ../gnomecard/card-editor.glade.h:22 +msgid "Identity" +msgstr "Thực thể" + +#: ../src/toolbar-factory.c:119 +msgid "Set identity to use for this message" +msgstr "Lập thực thể cần dùng cho thư này." + +#: ../src/toolbar-factory.c:122 +msgid "Toggle spell checker" +msgstr "Bật/Tắt kiểm tra chính tả" + +#: ../src/toolbar-factory.c:124 +msgid "Run a spell check" +msgstr "Kiểm tra chính tả" + +#: ../src/toolbar-factory.c:126 ../src/toolbar-factory.c:136 +#: ../glade/gbwidgets/gbdialog.c:331 ../list-ui.c:540 src/fe-gtk/search.c:120 +#: ../glade/gbwidgets/gbdialog.c:332 ../glade/search.glade.h:2 +#: ../widgets/gtk+.xml.in.h:35 install_gui.c:331 monthview_gui.c:511 +#: search_gui.c:585 weekview_gui.c:337 app/audioconfig.c:359 +#: app/gui-settings.c:496 app/gui.c:265 app/sample-editor.c:2207 +#: app/transposition.c:347 +msgid "Close" +msgstr "Đóng" + +#: ../src/toolbar-factory.c:127 +msgid "Close the compose window" +msgstr "Đóng cửa sổ soạn thảo" + +#: ../src/toolbar-factory.c:128 +msgid "" +"Toggle\n" +"new" +msgstr "" +"Bật/tắt\n" +"mới" + +#: ../src/toolbar-factory.c:129 +msgid "Toggle new message flag" +msgstr "Bật tắt đặt cờ thư mới." + +#: ../src/toolbar-factory.c:130 +msgid "Mark all" +msgstr "Đánh dấu hết" + +#: ../src/toolbar-factory.c:131 +msgid "Mark all messages in current mailbox" +msgstr "Đánh dấu mọi thư trong hộp thư hiện thời." + +#: ../src/toolbar-factory.c:132 +msgid "" +"All\n" +"headers" +msgstr "" +"Mọi\n" +"dòng đầu" + +#: ../src/toolbar-factory.c:133 +msgid "Show all headers" +msgstr "Hiện mọi dòng đầu." + +#: ../src/toolbar-factory.c:134 ../src/file-manager/fm-desktop-icon-view.c:706 +msgid "Empty Trash" +msgstr "Đổ Rác" + +#: ../src/toolbar-factory.c:137 +msgid "Close current mailbox" +msgstr "Đóng hộp thư hiện thời." + +#: ../src/toolbar-factory.c:138 +msgid "Msg Preview" +msgstr "Xem thử thư" + +#: ../src/toolbar-factory.c:139 +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:5 +msgid "Show preview pane" +msgstr "Hiện khung Xem thử" + +#: ../src/toolbar-factory.c:141 ../smime/lib/e-cert.c:424 +msgid "Sign" +msgstr "Ký" + +#: ../src/toolbar-factory.c:142 +msgid "Sign message using GPG" +msgstr "Ký tên thư bằng GPG" + +#: ../src/toolbar-factory.c:143 ../smime/lib/e-cert.c:425 +msgid "Encrypt" +msgstr "Mật mã hóa" + +#: ../src/toolbar-factory.c:144 +msgid "Encrypt message using GPG" +msgstr "Mật mã hóa thư bằng GPG." + +#: ../src/toolbar-factory.c:146 ../app/actions/edit-actions.c:69 +#: ../app/dialogs/dialogs.c:190 ../app/pdb/internal_procs.c:210 +#: ../src/menus.c:285 ../src/mainwin-menu.cc:84 +#, fuzzy +msgid "Undo" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Hoàn lại\n" +"#-#-#-#-# guikachu.vi.po (guikachu HEAD) #-#-#-#-#\n" +"Hoàn tác" + +#: ../src/toolbar-factory.c:147 +msgid "Undo most recent change" +msgstr "Hoàn lại thay đổi gần nhất" + +#: ../src/toolbar-factory.c:148 ui/galeon-bookmarks-editor-ui.xml.in.h:69 +#: ../app/actions/edit-actions.c:75 ../src/mainwin-menu.cc:88 +msgid "Redo" +msgstr "Làm lại" + +#: ../src/toolbar-factory.c:149 +msgid "Redo most recent change" +msgstr "Làm lại thay đổi gần nhất." + +#: ../src/toolbar-factory.c:150 +msgid "" +"Expunge\n" +"Deleted" +msgstr "" +"Xoá hẵn\n" +"đã xoá bỏ" + +#: ../src/toolbar-factory.c:151 +msgid "Expunge messages marked as deleted" +msgstr "Xoá hẵn các thư có nhãn Đã xoá bỏ." + +#: ../src/toolbar-factory.c:239 +#, c-format +msgid "Unknown toolbar icon \"%s\"" +msgstr "Không biết biểu tượng thanh công cụ « %s »." + +#: ../widgets/gtk+.xml.in.h:150 +msgid "Queue" +msgstr "Hàng đợi" + +#: ../src/toolbar-factory.c:358 +msgid "Queue this message for sending" +msgstr "Sắp hàng thư này để gởi" + +#: ../src/toolbar-prefs.c:123 +msgid "Customize Toolbars" +msgstr "Tùy chỉnh thanh công cụ" + +#: ../src/toolbar-prefs.c:144 src/prefsdlg.cpp:77 +msgid "Main window" +msgstr "Cửa sổ chính" + +#: ../src/toolbar-prefs.c:148 +msgid "Compose window" +msgstr "Cửa sổ soạn thảo" + +#: ../src/toolbar-prefs.c:152 +msgid "Message window" +msgstr "Cửa sổ thư" + +#: ../src/toolbar-prefs.c:154 +msgid "Toolbar options" +msgstr "Tùy chọn Thanh công cụ" + +#: ../src/toolbar-prefs.c:164 +msgid "_Wrap button labels" +msgstr "_Cuộn nhãn nút" + +#: ../src/toolbar-prefs.c:363 +#, c-format +msgid "Error displaying toolbar help: %s\n" +msgstr "Gặp lỗi khi hiển thị trợ giúp về thanh công cụ : %s\n" + +#: ../glade/gbwidgets/gbpreview.c:162 +msgid "Preview" +msgstr "Xem thử" + +#: ../src/toolbar-prefs.c:427 +msgid "_Restore toolbar to standard buttons" +msgstr "Phục hồi các nút chuẩn lên thanh công cụ." + +#: ../src/toolbar-prefs.c:446 +msgid "Available buttons" +msgstr "Nút có sẵn" + +#: ../src/toolbar-prefs.c:462 +msgid "Current toolbar" +msgstr "Thanh công cụ hiện có" + +#: ../src/toolbar-prefs.c:479 makeinfo/node.c:1524 ../ui/directions.glade.h:12 +#: ../storage/sunone-permissions-dialog.glade.h:28 +#: ../widgets/gtk+.xml.in.h:202 app/tracker-settings.c:282 +msgid "Up" +msgstr "Lên" + +#: ../src/toolbar-prefs.c:495 +#: ../plug-ins/script-fu/scripts/beveled-pattern-arrow.scm.h:1 +#: ../ui/directions.glade.h:3 ../storage/sunone-permissions-dialog.glade.h:16 +#: ../widgets/gtk+.xml.in.h:56 app/tracker-settings.c:285 +msgid "Down" +msgstr "Xuống" + +#: ../gnome-panel/panel-action-button.c:279 ../gdictsrc/dict.c:676 +#: ../gdictsrc/gdict-pref-dialog.c:700 ../gdictsrc/gdict-pref-dialog.c:747 +msgid "Cannot connect to server" +msgstr "Không thể kết nối đến máy phục vụ" + +msgid "Cannot read message" +msgstr "Không thể đọc thư." + +msgid "%s: could not get message stream." +msgstr "%s: không thể lấy luồng thư." + +msgid "Error setting flags on messages in mailbox %s" +msgstr "Gặp lỗi khi đặt cờ lên thư trong hộp thư %s." + +msgid "POP3 mailbox %s temp mailbox error:\n" +msgstr "Lỗi hộp thư tạm của hộp thư POP3 %s:\n" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. override the labels/defaults of the standard settings +#: src/fe-gtk/menu.c:1280 ../data/netgame.glade.h:8 src/fe-gtk/menu.c:1414 +#: ../camel/camel-sunone-provider.c:27 +msgid "_Server" +msgstr "Máy _phục vụ" + +msgid "Identity:" +msgstr "Thực thể:" + +msgid "Show address:" +msgstr "Hiện địa chỉ:" + +msgid "_From" +msgstr "_Từ" + +msgid "_To" +msgstr "Ch_o :" + +msgid "Could not create temporary file %s: " +msgstr "Không thể tạo tập tin tạm thời %s: " + +msgid "Could not get part: %s" +msgstr "Không thể lấy phân: %s" + +msgid "_Middle Name:" +msgstr "Tên _lót:" + +msgid "Run GnomeCard" +msgstr "Chạy Thẻ Gnome" + +msgid "New Address Book type:" +msgstr "Kiểu Sổ địa chỉ mới:" + +msgid "Balsa is not compiled with LDAP support" +msgstr "Trình Balsa đã được biên dịch không có hỗ trợ LDAP." + +msgid "_File Name" +msgstr "Tên _tập tin" + +msgid "Select path for VCARD address book" +msgstr "Chọn đường dẫn cho sổ địa chỉ dạng vCard" + +msgid "Select path for LDIF address book" +msgstr "Chọn đường dẫn cho sổ địa chỉ dạng LDIF." + +msgid "Match In" +msgstr "Khớp trong" + +msgid "Mailbox _Path:" +msgstr "_Đường dẫn hộp thư :" + +msgid "Mailbox Path" +msgstr "Đường dẫn hộp thư" + +msgid "(No identity set)" +msgstr "(Chưa đật thực thể)" + +msgid "C_hange..." +msgstr "_Đổi..." + +msgid "IMAP Server %s: %s" +msgstr "Máy phục vụ IMAP %s: %s" + +msgid "7 Bits" +msgstr "7-Bit" + +msgid "8 Bits" +msgstr "8-Bit" + +#: ../mail/mail-config.glade.h:113 ../mail/mail-config.glade.h:114 +msgid "Quoted" +msgstr "Trích dẫn" + +msgid "Remote SMTP Server" +msgstr "Máy phục vụ SMTP ở xa" + +#: ../libgda/gda-server-provider-extra.c:160 +#: ../storage/sunone-permissions-dialog.c:654 +msgid "User" +msgstr "Người dùng" + +msgid "Use TLS" +msgstr "Dùng TLS" + +msgid "Select a font to use" +msgstr "Chọn phông chữ cần dùng" + +#: ../data/glade/project-properties.glade.h:6 ../src/gnome-schedule.glade.h:52 +msgid "Select..." +msgstr "Chọn..." + +msgid "attach as reference" +msgstr "đính kèm dạng tham chiếu" + +msgid "attach as file" +msgstr "đính kèm dạng tập tin" + +msgid "" +"This file is not encoded in US-ASCII or UTF-8.\n" +"Please choose the charset used to encode the file.\n" +msgstr "" +"Tập tin này không được mã hóa theo US-ASCII hay UTF-8.\n" +"Hãy chọn bộ ký tự dùng để mã hóa tập tin.\n" + +msgid "_Attach as %s type \"%s\"" +msgstr "_Đính kèm dạng %s kiểu « %s »" + +msgid "" +"The message cannot be encoded in charset %s.\n" +"Please choose a language for this message.\n" +"For multi-language messages, choose UTF-8." +msgstr "" +"Không thể mã hóa thư này bằng bộ ký tự %s.\n" +"Hãy chọn một ngôn ngữ cho thư này.\n" +"Cho thư đa ngôn ngữ, và cho mọi ngôn\n" +"ngữ khi có thể, hãy chọn UTF-8." + +msgid "ukranian (koi)" +msgstr "U-cợ-rainh (KOI)" + +msgid "" +"Error placing messages from %s on %s\n" +"Messages are left in %s\n" +msgstr "" +"Gặp lỗi khi để thư từ %s lên %s.\n" +"Các thư còn lại trong %s.\n" + +msgid "POP3 temp mailbox %s was not removed (system error message: %s)" +msgstr "Chưa gỡ bỏ hộp thư tạm thời POP3 %s (thông điệp lỗi hệ thống: %s)." + +msgid "Source mailbox (%s) is readonly. Cannot move messages" +msgstr "" +"Hộp thư nguồn (%s) chỉ cho phép đọc. Không thể di chuyển các thông điệp." + +msgid "" +"Error writing to temporary file %s.\n" +"Check the directory permissions." +msgstr "" +"Gặp lỗi khi ghi vào tập tin tạm thời %s.\n" +"Hãy kiểm tra xem quyền hạn thư mục là đúng." + +msgid "" +"SMTP server refused connection.\n" +"Balsa by default uses submission service (587).\n" +"If you want to submit mail using relay service (25),specify it explicitly " +"via: \"host:smtp\".\n" +"Message is left in outbox." +msgstr "" +"Máy phục vụ SMTP đã từ chối kết nối.\n" +"Balsa theo mặc định dùng dịch vụ đệ trình (587).\n" +"Nếu muốn đệ trình thư bằng dịch vụ tiếp lại (25), hãy ghi rõ nó dứt khoát " +"bằng: \"host:smtp\".\n" +"Thư còn lại trong hộp Thư Đi." + +msgid "Please enter information about yourself." +msgstr "Hãy gõ thông tin về bạn." + +#: ../extensions/actions/action-properties.glade.h:11 ../src/drivel.glade.h:74 +#: ../ui/muds.glade.h:52 ../pan/server-ui.c:329 +msgid "_Name:" +msgstr "T_ên:" + +msgid "" +"You seem to be running Balsa for the first time. The following steps will " +"set up Balsa by asking a few simple questions. Once you have completed these " +"steps, you can always change them later in Balsa's preferences. If any files " +"or directories need to be created, it will be done so automatically.\n" +" Please check the about box in Balsa's main window for more information " +"about contacting the authors or reporting bugs." +msgstr "" +" Có vẻ như bạn chạy Balsa lần đầu tiên. Các bước sau đây sẽ thiết lập Balsa " +"bằng một số câu hỏi đơn giản. Ngay khi hoàn thành các bước này, bạn có thể " +"thay đổi chúng sau này trong mục Tùy thích của Balsa. Nếu bất ký tập tin hay " +"thư mục cần được tạo, nó sẽ được thực hiện tự động.\n" +" Hãy xem hộp giới thiệu trong cửa sổ chính của Balsa để biết thêm thông tin " +"về tác giả hay cách thông báo lỗi." + +#: ../pan/grouplist.c:993 +msgid "Total" +msgstr "Tổng" + +msgid "By _Date" +msgstr "Theo _ngày" + +msgid "Cannot access the message's body\n" +msgstr "Không thể truy cập thân thư.\n" + +msgid "Display message size as number of lines" +msgstr "Hiển thị kích cỡ của thư dạng số dòng." + +msgid "" +"Failed to initialise LDAP server.\n" +"Check that the servername is valid." +msgstr "" +"Việc khởi chạy máy phục vụ LDAP bị lỗi.\n" +"Hãy kiểm tra tên máy phục vụ có hợp lệ không." + +msgid "Couldn't set protocol version to LDAPv3." +msgstr "Không thể đặt phiên bản giao thức là LDAPv3." + +msgid "Couldn't enable TLS on the LDAP connection: %s" +msgstr "Không thể bật chạy TLS lên kết nối LDAP: %s" + +msgid "" +"Failed to bind to server: %s\n" +"Check that the server name is valid." +msgstr "" +"Việc đóng kết tới máy phục vụ bị lỗi: %s\n" +"Hãy kiểm tra tên máy chủ có hợp lệ không." + +msgid "Failed to do a search: %s.Check that the base name is valid." +msgstr "" +"Việc thực hiện tìm kiếm bị lỗi: %s. Hãy kiểm tra tên cơ bản có hợp lệ không." + +msgid "This certificate belongs to:\n" +msgstr "Chứng nhận này thuộc về:\n" + +msgid "*** ERROR: Mailbox Lock Exists: %s ***\n" +msgstr "••• LỖI: Hộp thư vẫn còn bị khoá: %s •••\n" + +msgid "*** ERROR: Mailbox Stream Closed: %s ***\n" +msgstr "••• LỖI: Luồng hộp thư bị đóng: %s •••\n" + +msgid "LibBalsaMailboxImap: Opening %s Refcount: %d\n" +msgstr "LibBalsaMailboxImap: Khi mở %s Đếm tham chiếu : %d\n" + +msgid "LibBalsaMailboxLocal: Opening %s Refcount: %d\n" +msgstr "LibBalsaMailboxLocal: Khi mở %s Đếm tham chiếu : %d\n" + +msgid "Couldn't open destination mailbox (%s) for writing" +msgstr "Không thể mở hộp thư đích (%s) để ghi." + +msgid "Couldn't open destination mailbox (%s) for copying" +msgstr "Không thể mở hộp thư đích (%s) để sao chép." + +msgid "connection error" +msgstr "lỗi kết nối" + +msgid "Could not run the delivery program (procmail)" +msgstr "Không thể chạy chương trình phát thư (procmail)." + +msgid "Could not open mailbox for spooling" +msgstr "Không thể mở hộp thư để cuộn vào ống" + +#: ../libgnomevfs/gnome-vfs-result.c:69 +msgid "Host not found" +msgstr "Không tìm thấy máy" + +#: src/common/util.c:301 +msgid "Connection refused" +msgstr "Kết nối bị từ chối." + +msgid "Unable to open sentbox - could not get IMAP server information" +msgstr "" +"Không thể mở hộp thư Đã Gởi — không thể lấy thông tin về máy phục vụ IMAP." + +msgid "" +"The mailbox \"%s\" does not appear to be valid.\n" +"Your system does not allow for creation of mailboxes\n" +"in /var/spool/mail. Balsa wouldn't function properly\n" +"until the system created the mailboxes. Please change\n" +"the mailbox path or check your system configuration." +msgstr "" +"Hình như hộp thư « %s » không hợp lệ.\n" +"Hệ thống của bạn không cho phép tạo hộp thư trong\n" +". Balsa sẽ không hoạt động đúng\n" +"cho tới khi hệ thống tạo được hộp thư. Hãy thay đổi\n" +"đường dẫn tới hộp thư hay kiểm tra cấu hình hệ thống." + +msgid "[-- Error: Could not display any parts of Multipart/Alternative! --]\n" +msgstr "[-- Lỗi: Không thể hiển thị phần nào của Đa phần/Xen kẽ ! --]\n" + +msgid "[-- Attachment #%d" +msgstr "[-- Đính kèm #%d" + +msgid "[-- Type: %s/%s, Encoding: %s, Size: %s --]\n" +msgstr "[-- Kiểu: %s/%s, Bộ ký tự : %s, Cỡ : %s --]\n" + +msgid "[-- Autoview using %s --]\n" +msgstr "[-- Xem tự động bằng %s --]\n" + +msgid "Invoking autoview command: %s" +msgstr "Đang gọi lệnh tự động xem: %s" + +msgid "Can't create filter" +msgstr "Không thể tạo bộ lọc." + +msgid "[-- Can't run %s. --]\n" +msgstr "[-- Không thể chạy %s. --]\n" + +msgid "[-- Autoview stderr of %s --]\n" +msgstr "[-- Tự động xem thiết bị lỗi chuẩn của %s --]\n" + +msgid "[-- Error: message/external-body has no access-type parameter --]\n" +msgstr "[-- Lỗi: thư/thân bên ngoài không có tham số về kiểu truy cập --]\n" + +msgid "[-- This %s/%s attachment " +msgstr "[-- Đính kèm %s/%s này " + +msgid "(size %s bytes) " +msgstr "(cỡ %s byte) " + +msgid "has been deleted --]\n" +msgstr "đã được xoá bỏ --]\n" + +msgid "[-- on %s --]\n" +msgstr "[-- vào %s --]\n" + +msgid "" +"[-- This %s/%s attachment is not included, --]\n" +"[-- and the indicated external source has --]\n" +"[-- expired. --]\n" +msgstr "" +"[-- Đính kèm %s/%s không được bao gồm, --]\n" +"[-- và nguồn bên ngoài được chỉ định --]\n" +"[-- đã hết hạn dùng. --]\n" + +msgid "[-- This %s/%s attachment is not included, --]\n" +msgstr "[-- Đính kèm %s/%s không được bao gồm, --]\n" + +msgid "[-- and the indicated access-type %s is unsupported --]\n" +msgstr "[-- và kiểu truy cập đã chỉ định %s không được hỗ trợ --]\n" + +msgid "Error: multipart/signed has no protocol." +msgstr "Lỗi: đa phần/đã ký không có giao thức." + +msgid "Error: multipart/encrypted has no protocol parameter!" +msgstr "Lỗi: đa phần/mật mã không có tham số cho giao thức." + +msgid "Unable to open temporary file!" +msgstr "• Không thể mở tập tin tạm thời. •" + +msgid "[-- %s/%s is unsupported " +msgstr "[-- %s/%s không được hỗ trợ. " + +msgid "(use '%s' to view this part)" +msgstr "(dùng '%s' để xem phần này)" + +msgid "(need 'view-attachments' bound to key!)" +msgstr "(cần đóng kết « xem đính kèm » tới phím!)" + +msgid "No authenticators available" +msgstr "Không có bộ xác thực sẵn sàng." + +msgid "Authenticating (anonymous)..." +msgstr "Đang xác thực (vô danh)..." + +msgid "Anonymous authentication failed." +msgstr "Việc xác thực vô danh bị lỗi." + +msgid "Authenticating (CRAM-MD5)..." +msgstr "Đang xác thực (CRAM-MD5)..." + +msgid "CRAM-MD5 authentication failed." +msgstr "Việc xác thực CRAM-MD5 bị lỗi." + +msgid "Authenticating (GSSAPI)..." +msgstr "Đang xác thực (GSSAPI)..." + +msgid "GSSAPI authentication failed." +msgstr "Việc xác thực GSSAPI bị lỗi." + +msgid "LOGIN disabled on this server." +msgstr "ĐĂNF NHẬP bị tắt trên máy phục vụ này." + +msgid "Logging in..." +msgstr "Đang đăng nhập..." + +msgid "Authenticating (SASL)..." +msgstr "Đang xác thực (SASL)..." + +msgid "SASL authentication failed." +msgstr "Việc xác thực SASL bị lỗi." + +msgid "%s is an invalid IMAP path" +msgstr "%s là đường dẫn IMAP không hợp lệ." + +msgid "Getting namespaces..." +msgstr "Đang lấy các miền tên..." + +msgid "Getting folder list..." +msgstr "Đang lấy danh sách thư mục..." + +#: ../mail/mail-stub-exchange.c:248 +msgid "No such folder" +msgstr "Không có thư mục như vậy" + +msgid "Create mailbox: " +msgstr "Tạo hộp thư : " + +msgid "Mailbox must have a name." +msgstr "Hộp thư phải có tên." + +msgid "Fatal error. Message count is out of sync!" +msgstr "Lỗi trầm trọng. Số thư không được đồng bộ !" + +msgid "Closing connection to %s..." +msgstr "Đang đóng kết nối tới %s..." + +msgid "This IMAP server is ancient. Mutt does not work with it." +msgstr "Máy phục vụ IMAP này là rất cũ nên trình Mutt không hoạt động với nó." + +msgid "Secure connection with TLS?" +msgstr "Kết nối bảo mật bằng TLS không?" + +msgid "Connecting to %s ..." +msgstr "Đang kết nối đến %s..." + +msgid "Could not negotiate TLS connection" +msgstr "Không thể thỏa thuận kết nối TLS." + +msgid "Selecting %s..." +msgstr "Đang chọn %s..." + +msgid "Unable to append to IMAP mailboxes at this server" +msgstr "Không thể phụ thêm vào hộp thư IMAP trên máy phục vụ này." + +msgid "Create %s?" +msgstr "Tạo %s không?" + +msgid "Closing connection to IMAP server..." +msgstr "Đang đóng kết nối tới máy phục vụ IMAP..." + +msgid "Saving message status flags... [%d/%d]" +msgstr "Đang lưu các cờ trạng thái thư... [%d/%d]" + +msgid "Expunging messages from server..." +msgstr "Đang xoá hẵn các thư ra máy phục vụ..." + +msgid "CLOSE failed" +msgstr "Việc ĐÓNG bị lỗi." + +msgid "Bad mailbox name" +msgstr "Tên hộp thư sai." + +msgid "Subscribing to %s..." +msgstr "Đang đăng ký với %s..." + +msgid "Unsubscribing to %s..." +msgstr "Đang bỏ đăng ký với %s..." + +msgid "Unable to fetch headers from this IMAP server version." +msgstr "Không thể lấy các dòng đầu từ phiên bản máy phục vụ IMAP này." + +msgid "Fetching message headers... [%d/%d]" +msgstr "Đang lấy các dòng đầu thư... [%d/%d]" + +msgid "Fetching message..." +msgstr "Đang lấy thư..." + +msgid "The message index is incorrect. Try reopening the mailbox." +msgstr "Chỉ mục thư là không đúng. Hãy cố mở lại hộp thư." + +msgid "Uploading message ..." +msgstr "Đang tải lên thư ..." + +msgid "Continue?" +msgstr "Tiếp tục không?" + +# Variable: don't translate / Biến: đừng dịch +msgid "%s [%s]\n" +msgstr "%s [%s]\n" + +msgid "Out of memory!" +msgstr "• Hết bộ nhớ. •" + +msgid "Reading %s... %d (%d%%)" +msgstr "Đang đọc %s... %d (%d%%)" + +msgid "Mailbox is corrupt!" +msgstr "• Hộp thư bị hỏng. •" + +msgid "Mailbox was corrupted!" +msgstr "• Hộp thư bị hỏng. •" + +msgid "Fatal error! Could not reopen mailbox!" +msgstr "• Lỗi nghiêm trọng: không thể mở lại hộp thư. •" + +msgid "sync: mbox modified, but no modified messages! (report this bug)" +msgstr "" +"đồng bộ : hộp thư mbox đã sửa đổi, nhưng không có thư đã sửa đổi (hãy thông " +"báo lỗi này)." + +msgid "Writing messages... %d (%d%%)" +msgstr "Đang ghi thư... %d (%d%%)" + +msgid "Committing changes..." +msgstr "Đang gài vào các thay đổi..." + +msgid "Write failed! Saved partial mailbox to %s" +msgstr "• Việc ghi bị lỗi. Đã lưu phần hộp thư vào %s. •" + +msgid "Could not reopen mailbox!" +msgstr "• Không thể mở lại tập tin. •" + +msgid "Connection to %s closed" +msgstr "Kết nối đến %s bị đóng." + +msgid "SSL is unavailable." +msgstr "SSL không sẵn sàng." + +msgid "Preconnect command failed." +msgstr "Lệnh tiền kết nối bị lỗi." + +msgid "Error talking to %s (%s)" +msgstr "Gặp lỗi khi nói với %s (%s)." + +msgid "Looking up %s..." +msgstr "Đang tra tìm %s..." + +msgid "Connecting to %s..." +msgstr "Đang kết nối đến %s..." + +msgid "Could not connect to %s (%s)." +msgstr "Không thể kết nối đến %s (%s)." + +msgid "Failed to find enough entropy on your system" +msgstr "" +"Không tìm thấy đủ en-tợ-rô-pi (tính trạng ngẫu nhiên) trong hệ thống của bạn." + +msgid "Filling entropy pool: %s...\n" +msgstr "Đang điền vũng en-tợ-rô-pi: %s...\n" + +msgid "%s has insecure permissions!" +msgstr "• %s có quyền hạn không bảo mật. •" + +msgid "SSL disabled due the lack of entropy" +msgstr "SSL bị tắt do thiếu en-tợ-rô-pi." + +#: src/files.c:117 +msgid "I/O error" +msgstr "Lỗi nhập/xuất" + +msgid "unspecified protocol error" +msgstr "lỗi giao thức không xác định" + +msgid "Unable to get certificate from peer" +msgstr "Không thể lấy chứng nhận từ ngang hàng." + +msgid "SSL connection using %s (%s)" +msgstr "Kết nối SSL bằng %s (%s)" + +msgid "[unable to calculate]" +msgstr "[không thể tính]" + +msgid "Server certificate is not yet valid" +msgstr "Chứng nhận máy phục vụ chưa hợp lệ." + +msgid "Server certificate has expired" +msgstr "Chứng nhận máy phục vụ đã hết hạn." + +msgid "Warning: Couldn't save certificate" +msgstr "Cảnh báo : không thể lưu chứng nhận." + +msgid "Certificate saved" +msgstr "Chứng nhận đã được lưu." + +msgid "This certificate belongs to:" +msgstr "Chứng nhận này thuộc về:" + +msgid "This certificate was issued by:" +msgstr "Chứng nhận này được phát hành bởi:" + +msgid " from %s" +msgstr " từ %s" + +msgid " to %s" +msgstr " đến %s" + +msgid "SSL Certificate check" +msgstr "Kiểm tra chứng nhận SSL" + +msgid "(r)eject, accept (o)nce, (a)ccept always" +msgstr "(t)ừ chối, chấp nhận (m)ột lần, (l)uôn chấp nhận" + +msgid "(r)eject, accept (o)nce" +msgstr "(t)ừ chối, chấp nhận (m)ột lần" + +msgid "Exit " +msgstr "Thoát " + +#: src/fe-gtk/editlist.c:380 web/template/auth.tpl:4 src/floatwin.cpp:147 +#: jpilot.c:386 +msgid "Help" +msgstr "Trợ giúp" + +msgid "Reading %s... %d" +msgstr "Đang đọc %s... %d" + +msgid "Lock count exceeded, remove lock for %s?" +msgstr "Vượt quá tổng số khoá, gỡ bỏ khoá cho %s không?" + +msgid "Can't dotlock %s.\n" +msgstr "Không thể khoá chấm %s.\n" + +msgid "Couldn't lock %s\n" +msgstr "Không thể khoá '%s\n" + +msgid "Writing %s..." +msgstr "Đang ghi %s..." + +msgid "Could not synchronize mailbox %s!" +msgstr "• Không thể đồng bộ hóa hộp thư %s. •" + +msgid "Mailbox is unchanged." +msgstr "Hộp thư chưa thay đổi." + +msgid "%d kept, %d moved, %d deleted." +msgstr "%d đã giữ lại, %d đã di chuyển, %d đã xoá bỏ." + +msgid "%d kept, %d deleted." +msgstr "%d đã giữ, %d đã xoá bỏ." + +msgid " Press '%s' to toggle write" +msgstr " Nhấn « %s » để bật/tắt ghi" + +msgid "Use 'toggle-write' to re-enable write!" +msgstr "• Dùng « bật/tắt ghi » để bật lại khả năng ghi. •" + +msgid "Mailbox is marked unwritable. %s" +msgstr "Hộp thư có nhãn không có khả năng ghi. %s" + +msgid "Mailbox is read-only." +msgstr "Hộp thư là chỉ-đọc." + +msgid "Purge %d deleted message?" +msgstr "Tẩy %d thư đã xoá bỏ không?" + +msgid "%d kept." +msgstr "%d đã giữ" + +msgid "multipart message has no boundary parameter!" +msgstr "• Thư đa phần không có tham số ranh giới •" + +msgid "No boundary parameter found! [report this error]" +msgstr "• Không tìm thấy tham số ranh giới! [hãy thông báo lỗi này] •" + +msgid "%s no longer exists!" +msgstr "• %s không còn tồn tại lại. •" + +msgid "Can't stat %s: %s" +msgstr "Không thể lấy các thông tin về %s: %s" + +msgid "%s isn't a regular file." +msgstr "%s không phải là tập tin chuẩn." + +msgid "Output of the delivery process" +msgstr "Kết xuất của tiến trình phát thư." + +msgid "Remote IMAP folder set" +msgstr "Đã lập thư mục IMAP ở xa." + +msgid "Use SS_L (IMAPS)" +msgstr "Dùng SS_L (IMAPS)" + +msgid "Balsa Information" +msgstr "Thông tin Balsa" + +msgid "Oooop! mailbox not found in balsa_app.mailbox nodes?\n" +msgstr "" +"Ối! không tìm thấy hộp thư trong các nút « balsa_app.mailbox » (hộp thư ứng " +"dụng balsa)?\n" + +msgid "No value set" +msgstr "Chưa đặt giá trị." + +msgid "Use _APOP Authentication" +msgstr "Dùng cách xác thực _APOP" + +msgid "Use SS_L (pop3s)" +msgstr "Dùng SS_L (pop3s)" + +#: ../glade/property.c:5115 ../pan/filter-edit-ui.c:809 ../pan/gui.c:1163 +#: ../src/mainwin-menu.cc:50 category.c:832 +msgid "New" +msgstr "Mới" + +msgid "Delete the current message" +msgstr "Xoá bỏ thư hiện thời" + +msgid "Undelete the message" +msgstr "Hủy xoá bỏ thư" + +#. ../lisp/sawfish/wm/menus.jl +msgid "_Toggle" +msgstr "_Bật/tắt" + +msgid "Co_mmit Current" +msgstr "_Gài vào điều hiện thời" + +msgid "Commit _All" +msgstr "Gài vào _hết" + +msgid "Commit the changes in all mailboxes" +msgstr "Gài vào các thay đổi trong mọi hộp thư." + +msgid "Edit/Apply _Filters" +msgstr "Sửa/Áp dụng bộ _lọc" + +msgid "Filter the content of the selected mailbox" +msgstr "Lọc nội dung của hộp thư được chọn." + +msgid "" +"Unable to Open Mailbox!\n" +"Please check the mailbox settings." +msgstr "" +"• Không thể mở hộp thư. •\n" +"Hãy kiểm tra xem thiết lập hộp thư là đúng." + +msgid "Copyright (C) 1997-2002" +msgstr "Bản quyền © năm 1997-2002" + +msgid "You have received 1 new message." +msgstr "Bạn mới nhận 1 thư mới." + +msgid "External editor command:" +msgstr "Lệnh bộ soạn thảo bên ngoại:" + +msgid "Message window title format:" +msgstr "Dạng thức tựa cửa sổ thư :" + +msgid "Mailbox Colors" +msgstr "Màu sắc hộp thư" + +msgid "Mailbox with unread messages color" +msgstr "Màu của hộp thư có thư chưa đọc" + +msgid "Delete immediately and irretrievably" +msgstr "Xoá bỏ ngay lập tức và hoàn toàn" + +#: ../shell/ev-sidebar-links.c:304 +msgid "Print..." +msgstr "In..." + +msgid "_Reflow Paragraph" +msgstr "Cuộn _lại đoạn văn" + +msgid "R_eflow Message" +msgstr "C_uộn lại thư" + +#: ../interfaces/users.glade.in.h:49 +msgid "_Comments" +msgstr "_Ghi chú" + +msgid "_Keywords" +msgstr "Từ _khoá" + +#: ../src/util.c:399 +msgid "English" +msgstr "Tiếng Anh" + +#: ui/bookmarks-editor.glade.h:48 +msgid "UTF-8" +msgstr "UTF-8" + +msgid "_A-J" +msgstr "_A-J" + +msgid "_K-Z" +msgstr "_K-Z" + +#: ../src/header_stuff.c:469 ../glade/gbwidgets/gbaboutdialog.c:106 +#: ../glade/gnome/gnomeabout.c:139 +msgid "Comments:" +msgstr "Ghi chú :" + +#: ../src/blam.glade.h:17 ../xpdf/gpdf-properties-dialog.glade.h:4 +msgid "Keywords:" +msgstr "Từ khoá:" + +msgid "Sorry, no semicolons are allowed in the name!\n" +msgstr "Xin lỗi, không chấp nhận dấu chấm phẩy trong tên.\n" + +msgid "Cancel this message" +msgstr "Thôi thư này" + +#. ../lisp/sawfish/wm/customize.jl +msgid "Customize" +msgstr "Tùy chỉnh" + +msgid "_File name" +msgstr "T_ên tập tin" + +msgid "Multiple mailboxes named \"%s\"" +msgstr "Có nhiều thư có cùng tên « %s »." + +#: ../plug-ins/common/ccanalyze.c:405 +#, c-format +msgid "Filename: %s" +msgstr "Tên tập tin: %s" + +msgid "Use SS_L (imaps)" +msgstr "Dùng SS_L (imaps)" + +msgid "Next part in Message" +msgstr "Phần kế trong thư" + +msgid "Previous part in Message" +msgstr "Phần trước trong thư" + +msgid "Reflow messages of type `text/plain; format=flowed'" +msgstr "Cuộn lại các thư dạng « chữ/thô; dạng thức=đã trôi chảy »" + +msgid "Send message as type `text/plain; format=flowed'" +msgstr "Gởi thư dạng « chữ/thô; dạng thức=đã trôi chảy »" + +#: ../grecord/src/gsr-window.c:1032 ../grecord/src/gsr-window.c:1953 +#: ../src/record.c:187 ../plug-ins/imagemap/imap_settings.c:94 +msgid "Filename:" +msgstr "Tên tập tin:" + +msgid "_Always Queue Sent Mail" +msgstr "_Luôn sắp hàng thư đã gởi" + +msgid "Date: %s\n" +msgstr "Ngày: %s\n" + +msgid "From: %s\n" +msgstr "Từ : %s\n" + +msgid "To: %s\n" +msgstr "Cho : %s\n" + +# Literal: don't translate / Nghĩa chữ : đừng dịch +msgid "CC: %s\n" +msgstr "CC: %s\n" + +msgid "Delete messages from the trash mailbox" +msgstr "Xoá bỏ thư ra hộp thư Rác" + +msgid "Address _Book Name" +msgstr "T_ên sổ địa chỉ" + +msgid "Connecting with \"%s\"..." +msgstr "Đang kết nối đến « %s »..." + +msgid "Tunnel error talking to %s: %s" +msgstr "Gặp lỗi đường hầm khi nói với %s: %s" + +msgid "Source mailbox (%s) is readonly. Cannot move message" +msgstr "Hộp thư nguồn (%s) chỉ cho phép đọc nên không thể di chuyển thư." + +msgid "Preview Font" +msgstr "Phông chữ xem thử" + +msgid "Preview pane" +msgstr "Khung xem thử" + +msgid "Card Name:" +msgstr "Tên thẻ:" + +#: ../addressbook/printing/e-contact-print.glade.h:4 ../pan/gui-notebook.c:57 +msgid "Body" +msgstr "Thân" + +msgid "Selected condition search type:" +msgstr "Kiểu tìm kiếm điều kiện đã chọn:" + +#: ../Pyblio/GnomeUI/Search.py:86 +#: ../storage/sunone-add-permission-dialog.glade.h:3 +#: ../storage/sunone-permissions-dialog.c:585 address_gui.c:2705 +#: category.c:421 category.c:844 datebook_gui.c:4376 memo_gui.c:1557 +#: todo_gui.c:2174 Expense/expense.c:1646 KeyRing/keyring.c:1612 +#: app/envelope-box.c:893 app/playlist.c:533 +msgid "Delete" +msgstr "Xoá bỏ" + +msgid "Filters may not be correct" +msgstr "Bộ lọc có thể không đúng." + +#: ../shell/rb-statusbar.c:258 app/gui.c:1769 app/gui.c:1806 +msgid "Loading..." +msgstr "Đang tải..." + +msgid "" +"Could not open external query address book %s while trying to parse output " +"from: %s" +msgstr "" +"Không thể mở sổ địa chỉ truy vấn bên ngoài %s trong khi cố phân tách dữ liệu " +"xuất từ : %s" + +msgid "Could not add address to address book %s while trying to execute: %s" +msgstr "Không thể thêm địa chỉ vào sổ địa chỉ %s trong khi cố thực thi: %s" + +msgid "Could not stat ldif address book: %s" +msgstr "Không thể lấy các thông tin về sổ địa chỉ LDIF: %s" + +msgid "Could not open LDIF address book %s." +msgstr "Không thể mở sổ địa chỉ LDIF %s." + +msgid "Could not stat vcard address book: %s" +msgstr "Không thể lấy các thông tin về sổ địa chỉ vCard: %s" + +msgid "Could not open vCard address book %s." +msgstr "Không thể mở sổ địa chỉ vCard %s." + +msgid "Cannot open vCard address book %s for saving\n" +msgstr "Không thể mở sổ địa chỉ vCard %s để lưu\n" + +msgid "No such address book type: %s" +msgstr "Không có kiểu sổ địa chỉ như vậy: %s" + +msgid "Could not create a address book of type %s" +msgstr "Không thể tạo sổ địa chỉ kiểu %s." + +msgid "_Customize..." +msgstr "Tù_y chỉnh..." + +msgid "" +"The attachment pixmap (%s) cannot be used.\n" +" %s" +msgstr "" +"Không thể sử dụng sơ đồ điểm ảnh của đính kèm (%s).\n" +"%s" + +msgid "" +"Default attachment pixmap (attachment.png) cannot be found:\n" +"Your balsa installation is corrupted." +msgstr "" +"Không thể tìm thấy sơ đồ điểm ảnh (pixmap) đính kèm mặc định (attachment." +"png):\n" +"Bản cài đặt Balsa bị hỏng." + +#: ../glade/config1.glade.h:14 ../glade/fields1.glade.h:19 +#: ../widgets/gtk+.xml.in.h:179 ../src/orca/rolenames.py:453 +msgid "Text" +msgstr "Chữ" + +#: ../src/file-manager/fm-icon-view.c:2721 ../widgets/gtk+.xml.in.h:98 +msgid "Icons" +msgstr "Biểu tượng" + +#: src/fe-gtk/menu.c:1411 ../plug-ins/print/gimp_main_window.c:621 +#: ../widgets/gtk+.xml.in.h:15 +msgid "Both" +msgstr "Cả hai" + +msgid "load program" +msgstr "tải chương trình" + +msgid "save program" +msgstr "lưu chương trình" + +#: ../src/mlview-attribute-picker.cc:163 address_gui.c:697 alarms.c:226 +#: category.c:420 category.c:871 category.c:911 datebook_gui.c:669 +#: datebook_gui.c:1149 export_gui.c:339 jpilot.c:363 jpilot.c:409 jpilot.c:477 +#: jpilot.c:521 jpilot.c:968 jpilot.c:1869 memo_gui.c:526 password.c:352 +#: restore_gui.c:307 todo_gui.c:661 utils.c:1084 utils.c:1256 +#: KeyRing/keyring.c:1322 src/silc-command-reply.c:787 +#: app/sample-editor.c:1655 app/sample-editor.c:1970 +msgid "OK" +msgstr "Được" + +msgid "" +"Can not convert %s, falling back to US-ASCII.\n" +"Some characters may be printed incorrectly." +msgstr "" +"Không thể chuyển đổi %s nên quay lại US-ASCII.\n" +"Có thể không in ra đúng một số ký tự." + +msgid "" +"Balsa could not find font %s\n" +"Printing is not possible" +msgstr "" +"Không thể tìm thấy phông chữ %s.\n" +"Như thế thì không in ra được." + +msgid "Reply..." +msgstr "Trả lời..." + +msgid "Store Address..." +msgstr "Lưu địa chỉ..." + +#: address_gui.c:2711 datebook_gui.c:4382 memo_gui.c:1563 todo_gui.c:2180 +msgid "Undelete" +msgstr "Phục hồi" + +msgid "Address Book Configuration" +msgstr "Cấu hình Sổ địa chỉ" + +#: ../Pyblio/GnomeUI/Config.py:432 +msgid "Update" +msgstr "Cập nhật" + +msgid "Memory allocation error" +msgstr "Lỗi cấp phát bộ nhớ" + +msgid "All headers" +msgstr "Mọi dòng đầu" + +#: addr2line.c:73 +#, c-format +msgid "Usage: %s [option(s)] [addr(s)]\n" +msgstr "Cách sử dụng: %s [tùy_chọn...] [địa_chỉ...)]\n" + +#: addr2line.c:74 +#, c-format +msgid " Convert addresses into line number/file name pairs.\n" +msgstr " Chuyển đổi địa chỉ sang cặp số thứ tự dòng/tên tập tin.\n" + +#: addr2line.c:75 +#, c-format +msgid "" +" If no addresses are specified on the command line, they will be read from " +"stdin\n" +msgstr "" +"Nếu chưa ghi rõ địa chỉ trên dòng lệnh, sẽ đọc chúng từ thiết bị nhập chuẩn\n" + +#: addr2line.c:76 +#, c-format +msgid "" +" The options are:\n" +" -b --target= Set the binary file format\n" +" -e --exe= Set the input file name (default is a.out)\n" +" -s --basenames Strip directory names\n" +" -f --functions Show function names\n" +" -C --demangle[=style] Demangle function names\n" +" -h --help Display this information\n" +" -v --version Display the program's version\n" +"\n" +msgstr "" +" Tùy chọn:\n" +" -b --target= \tLập khuôn dạng tập tin nhị phân (_đích_)\n" +" -e --exe= \tLập tên tập tin nhập (mặc định là )\n" +"\t\t\t\t\t\t\t\t(_chương trình chạy được_)\n" +" -s --basenames\t\tTước các tên thư mục (_các tên cơ bản_)\n" +" -f --functions \tHiện tên _các chức năng_\n" +" -C --demangle[=kiểu_dáng] \t_Tháo gỡ_ tên chức năng\n" +" -h --help \tHiện thông tin _trợ giúp_ này\n" +" -v --version \tHiện _phiên bản_ của chương trình\n" +"\n" + +#: sysdump.c:655 windres.c:672 lexsup.c:1547 gprof.c:176 +#, c-format +msgid "Report bugs to %s\n" +msgstr "Hãy thông báo lỗi nào cho %s\n" + +#: addr2line.c:241 +#, c-format +msgid "%s: can not get addresses from archive" +msgstr "%s: không thể lấy địa chỉ từ kho" + +#: addr2line.c:311 nm.c:1519 objdump.c:2848 +#, c-format +msgid "unknown demangling style `%s'" +msgstr "không biết kiểu dáng tháo gõ « %s »" + +#: ar.c:205 +#, c-format +msgid "no entry %s in archive\n" +msgstr "không có mục nhập %s trong kho\n" + +#: ar.c:221 +#, c-format +msgid "" +"Usage: %s [emulation options] [-]{dmpqrstx}[abcfilNoPsSuvV] [member-name] " +"[count] archive-file file...\n" +msgstr "" +"Cách sử dụng: %s [tùy chọn mô phỏng] [-]{dmpqrstx}[abcfilNoPsSuvV] [tên " +"thành viên] [số đếm] tập_tin_kho tập_tin...\n" + +#: ar.c:224 +#, c-format +msgid " %s -M [\n" +"\n" +msgstr "" +"\n" +"<%s>\n" +"\n" + +#: ar.c:806 ar.c:873 +#, c-format +msgid "%s is not a valid archive" +msgstr "%s không phải là một kho hợp lệ" + +#: ar.c:841 +#, c-format +msgid "stat returns negative size for %s" +msgstr "việc stat (lấy các thông tin) trả gởi kích cỡ âm cho %s" + +#: ar.c:1059 +#, c-format +msgid "No member named `%s'\n" +msgstr "Không có thành viên tên « %s »\n" + +#: ar.c:1109 +#, c-format +msgid "no entry %s in archive %s!" +msgstr "không có mục nhập %s trong kho %s." + +#: ar.c:1246 +#, c-format +msgid "%s: no archive map to update" +msgstr "%s: không có ánh xạ kho cần cập nhật" + +#: arsup.c:83 +#, c-format +msgid "No entry %s in archive.\n" +msgstr "Không có mục nhập %s trong kho.\n" + +#: arsup.c:109 +#, c-format +msgid "Can't open file %s\n" +msgstr "Không thể mở tập tin %s\n" + +#: arsup.c:162 +#, c-format +msgid "%s: Can't open output archive %s\n" +msgstr "%s: Không thể mở kho xuất %s\n" + +#: arsup.c:179 +#, c-format +msgid "%s: Can't open input archive %s\n" +msgstr "%s: Không thể mở kho nhập %s\n" + +#: arsup.c:188 +#, c-format +msgid "%s: file %s is not an archive\n" +msgstr "%s: tập tin %s không phải là kho\n" + +#: arsup.c:227 +#, c-format +msgid "%s: no output archive specified yet\n" +msgstr "%s: chưa ghi rõ kho xuất\n" + +#: arsup.c:247 arsup.c:285 arsup.c:327 arsup.c:347 arsup.c:413 +#, c-format +msgid "%s: no open output archive\n" +msgstr "%s: không có kho xuất đã mở\n" + +#: arsup.c:258 arsup.c:368 arsup.c:394 +#, c-format +msgid "%s: can't open file %s\n" +msgstr "%s: không thể mở tập tin %s\n" + +#: arsup.c:312 arsup.c:390 arsup.c:471 +#, c-format +msgid "%s: can't find module file %s\n" +msgstr "%s: không tìm thấy tập tin mô-đun %s\n" + +#: arsup.c:422 +#, c-format +msgid "Current open archive is %s\n" +msgstr "Kho đã mở hiện thời là %s\n" + +#: arsup.c:446 +#, c-format +msgid "%s: no open archive\n" +msgstr "%s: không có kho đã mở\n" + +#: binemul.c:37 +#, c-format +msgid " No emulation specific options\n" +msgstr " Không có tùy chọn đặc trưng cho mô phỏng\n" + +#. Macros for common output. +#: binemul.h:42 +#, c-format +msgid " emulation options: \n" +msgstr " tùy chọn mô phỏng:\n" + +#: bucomm.c:109 +#, c-format +msgid "can't set BFD default target to `%s': %s" +msgstr "không thể lập đích mặc định BFD thành « %s »: %s" + +#: bucomm.c:120 +#, c-format +msgid "%s: Matching formats:" +msgstr "%s: khuôn dạng khớp:" + +#: bucomm.c:135 +#, c-format +msgid "Supported targets:" +msgstr "Đích hỗ trợ :" + +#: bucomm.c:137 lexsup.c:1530 +#, c-format +msgid "%s: supported targets:" +msgstr "%s: đích hỗ trợ :" + +#: bucomm.c:153 +#, c-format +msgid "Supported architectures:" +msgstr "Kiến trúc hỗ trợ :" + +#: bucomm.c:155 +#, c-format +msgid "%s: supported architectures:" +msgstr "%s: kiến trúc hỗ trợ :" + +#: bucomm.c:348 +#, c-format +msgid "BFD header file version %s\n" +msgstr "Phiên bản tập tin đầu BFD %s\n" + +#: bucomm.c:449 +#, c-format +msgid "%s: bad number: %s" +msgstr "%s: số sai: %s" + +#: bucomm.c:466 strings.c:386 +#, c-format +msgid "'%s': No such file" +msgstr "« %s »: không có tập tin như vậy" + +#: bucomm.c:468 strings.c:388 +#, c-format +msgid "Warning: could not locate '%s'. reason: %s" +msgstr "Cảnh báo : không thể định vị « %s ». Lý do : %s" + +#: bucomm.c:472 +#, c-format +msgid "Warning: '%s' is not an ordinary file" +msgstr "Cảnh báo : « %s » không phải là một tập tin chuẩn" + +#: coffdump.c:105 +#, c-format +msgid "#lines %d " +msgstr "#dòng %d " + +#: coffdump.c:459 sysdump.c:648 +#, c-format +msgid "Usage: %s [option(s)] in-file\n" +msgstr "Cách sử dụng: %s [tùy_chọn...] tập_tin_nhập\n" + +#: coffdump.c:460 +#, c-format +msgid " Print a human readable interpretation of a SYSROFF object file\n" +msgstr "" +"In ra lời thông dịch cho phép người đọc của tập tin đối tượng SYSROFF\n" + +#: coffdump.c:461 +#, c-format +msgid "" +" The options are:\n" +" -h --help Display this information\n" +" -v --version Display the program's version\n" +"\n" +msgstr "" +"Tùy chọn:\n" +" -h, --help hiển thị _trợ giúp_ này\n" +" -v --version hiển thị _phiên bản_ của chương trình\n" + +#: coffdump.c:527 srconv.c:1819 sysdump.c:710 +msgid "no input file specified" +msgstr "chưa ghi rõ tập tin nhập" + +#: debug.c:648 +msgid "debug_add_to_current_namespace: no current file" +msgstr "" +"debug_add_to_current_namespace: (gỡ lỗi thêm vào vùng tên hiện có) không có " +"tập tin hiện thời" + +#: debug.c:727 +msgid "debug_start_source: no debug_set_filename call" +msgstr "" +"debug_start_source: (gỡ lỗi bắt đầu nguồn) không có cuộc gọi kiểu « " +"debug_set_filename » (gỡ lỗi lập tên tập tin)" + +#: debug.c:783 +msgid "debug_record_function: no debug_set_filename call" +msgstr "" +"debug_record_function: (gỡ lỗi ghi lưu chứa năng) không có cuộc gọi kiểu « " +"debug_set_filename » (gỡ lỗi lập tên tập tin)" + +#: debug.c:835 +msgid "debug_record_parameter: no current function" +msgstr "" +"debug_record_parameter: (gỡ lỗi ghi lưu tham số) không có chức năng hiện thời" + +#: debug.c:867 +msgid "debug_end_function: no current function" +msgstr "" +"debug_end_function: (gỡ lỗi kết thúc chức năng) không có chức năng hiện thời" + +#: debug.c:873 +msgid "debug_end_function: some blocks were not closed" +msgstr "" +"debug_end_function: (gỡ lỗi kết thúc chức năng) một số khối chưa được đóng" + +#: debug.c:901 +msgid "debug_start_block: no current block" +msgstr "debug_start_block: (gỡ lỗi bắt đầu khối) không có khối hiện thời" + +#: debug.c:937 +msgid "debug_end_block: no current block" +msgstr "debug_end_block: (gỡ lỗi kết thúc khối) không có khối hiện thời" + +#: debug.c:944 +msgid "debug_end_block: attempt to close top level block" +msgstr "debug_end_block: (gỡ lỗi kết thúc khối) cố đóng khối cấp đầu" + +#: debug.c:967 +msgid "debug_record_line: no current unit" +msgstr "debug_record_line: (gỡ lỗi ghi lưu dòng) không có đơn vị hiện thời" + +#. FIXME +#: debug.c:1020 +msgid "debug_start_common_block: not implemented" +msgstr "debug_start_common_block: not implemented" + +#. FIXME +#: debug.c:1031 +msgid "debug_end_common_block: not implemented" +msgstr "debug_end_common_block: not implemented" + +#. FIXME. +#: debug.c:1115 +msgid "debug_record_label: not implemented" +msgstr "debug_record_label: not implemented" + +#: debug.c:1137 +msgid "debug_record_variable: no current file" +msgstr "" +"debug_record_variable: (gỡ lỗi ghi lưu biến) không có tập tin hiện thờino " +"current file" + +#: debug.c:1665 +msgid "debug_make_undefined_type: unsupported kind" +msgstr "" +"debug_make_undefined_type: (gỡ lỗi tạo kiểu chưa được định nghĩa) kiểu chưa " +"được hỗ trợ" + +#: debug.c:1842 +msgid "debug_name_type: no current file" +msgstr "debug_name_type: no current file" + +#: debug.c:1887 +msgid "debug_tag_type: no current file" +msgstr "" +"debug_tag_type: (gỡ lỗi kiểu thẻ) không có tập tin hiện thờiLưu tập tin hiện" + +#: debug.c:1895 +msgid "debug_tag_type: extra tag attempted" +msgstr "debug_tag_type: (gỡ lỗi kiểu thẻ) đã cố thẻ thêm" + +#: debug.c:1932 +#, c-format +msgid "Warning: changing type size from %d to %d\n" +msgstr "Cảnh báo : đang thay đổi kích cỡ kiểu từ %d đến %d\n" + +#: debug.c:1954 +msgid "debug_find_named_type: no current compilation unit" +msgstr "" +"debug_find_named_type: (gỡ lỗi tìm kiểu tên đã cho) không có đơn vị biên " +"dịch hiện thời" + +#: debug.c:2057 +#, c-format +msgid "debug_get_real_type: circular debug information for %s\n" +msgstr "" +"debug_get_real_type: (gỡ lỗi lấy kiểu thật) thông tin gỡ lỗi vòng cho %s\n" + +#: debug.c:2484 +msgid "debug_write_type: illegal type encountered" +msgstr "debug_write_type: (gỡ lỗi ghi kiểu) gặp kiểu không được phép" + +#: dlltool.c:773 dlltool.c:797 dlltool.c:826 +#, c-format +msgid "Internal error: Unknown machine type: %d" +msgstr "Lỗi nội bộ : không biết kiểu máy: %d" + +#: dlltool.c:862 +#, c-format +msgid "Can't open def file: %s" +msgstr "Không thể mở tập tin định nghĩa: %s" + +#: dlltool.c:867 +#, c-format +msgid "Processing def file: %s" +msgstr "Đang xử lý tập tin định nghĩa: %s" + +#: dlltool.c:871 +msgid "Processed def file" +msgstr "Đã xử lý tập tin định nghĩa" + +#: dlltool.c:895 +#, c-format +msgid "Syntax error in def file %s:%d" +msgstr "Gặp lỗi cú pháp trong tập tin định nghĩa %s:%d" + +#: dlltool.c:930 +#, c-format +msgid "%s: Path components stripped from image name, '%s'." +msgstr "%s: các thành phần đường dẫn bị tước ra tên ảnh, « %s »." + +#: dlltool.c:939 +#, c-format +msgid "NAME: %s base: %x" +msgstr "TÊN: %s cơ bản: %x" + +#: dlltool.c:942 dlltool.c:958 +msgid "Can't have LIBRARY and NAME" +msgstr "Không cho phép dùng cả THƯ VIÊN lẫn TÊN đều" + +#: dlltool.c:955 +#, c-format +msgid "LIBRARY: %s base: %x" +msgstr "THƯ VIÊN: %s cơ bản: %x" + +#: dlltool.c:1191 resrc.c:269 +#, c-format +msgid "wait: %s" +msgstr "đợi: %s" + +#: dlltool.c:1196 dllwrap.c:418 resrc.c:274 +#, c-format +msgid "subprocess got fatal signal %d" +msgstr "tiến trình con đã nhận tín hiệu nghiệm trọng %d" + +#: dlltool.c:1202 dllwrap.c:425 resrc.c:281 +#, c-format +msgid "%s exited with status %d" +msgstr "%s đã thoát với trạng thái %d" + +#: dlltool.c:1233 +#, c-format +msgid "Sucking in info from %s section in %s" +msgstr "Đang kéo vào thông tin từ phần %s trong %s..." + +#: dlltool.c:1358 +#, c-format +msgid "Excluding symbol: %s" +msgstr "Đang loại trừ ký hiệu : %s" + +#: dlltool.c:1447 dlltool.c:1458 nm.c:998 nm.c:1009 +#, c-format +msgid "%s: no symbols" +msgstr "%s: không có ký hiệu" + +#. FIXME: we ought to read in and block out the base relocations. +#: dlltool.c:1484 +#, c-format +msgid "Done reading %s" +msgstr "Đã đọc xong %s" + +#: dlltool.c:1494 +#, c-format +msgid "Unable to open object file: %s" +msgstr "Không thể mở tập tin đối tượng: %s" + +#: dlltool.c:1497 +#, c-format +msgid "Scanning object file %s" +msgstr "Đang quét tập tin đối tượng %s..." + +#: dlltool.c:1512 +#, c-format +msgid "Cannot produce mcore-elf dll from archive file: %s" +msgstr "Không thể cung cấp « mcore-elf dll » từ tập tin kho: %s" + +#: dlltool.c:1598 +msgid "Adding exports to output file" +msgstr "Đang thêm các việc xuất vào nhóm kết xuất..." + +#: dlltool.c:1646 +msgid "Added exports to output file" +msgstr "Đã thêm các việc xuất vào nhóm kết xuất" + +#: dlltool.c:1767 +#, c-format +msgid "Generating export file: %s" +msgstr "Đang tạo ra tập tin xuất: %s" + +#: dlltool.c:1772 +#, c-format +msgid "Unable to open temporary assembler file: %s" +msgstr "Không thể mở tập tin dịch mã số tạm thời: %s" + +#: dlltool.c:1775 +#, c-format +msgid "Opened temporary file: %s" +msgstr "Đã mở tập tin tạm thời: %s" + +#: dlltool.c:1997 +msgid "Generated exports file" +msgstr "Đã tạo ra tập tin xuất" + +#: dlltool.c:2203 +#, c-format +msgid "bfd_open failed open stub file: %s" +msgstr "bfd_open không mở được tập tin stub: %s" + +#: dlltool.c:2206 +#, c-format +msgid "Creating stub file: %s" +msgstr "Đang tạo tập tin stub: %s" + +#: dlltool.c:2588 +#, c-format +msgid "failed to open temporary head file: %s" +msgstr "lỗi mở tập tin đầu tạm: %s" + +#: dlltool.c:2647 +#, c-format +msgid "failed to open temporary tail file: %s" +msgstr "lỗi mở tập tin đuôi tạm: %s" + +#: dlltool.c:2714 +#, c-format +msgid "Can't open .lib file: %s" +msgstr "Không thể mở tập tin « .lib » (thư viên): %s" + +#: dlltool.c:2717 +#, c-format +msgid "Creating library file: %s" +msgstr "Đang tạo tập tin thư viên: %s" + +#: dlltool.c:2800 dlltool.c:2806 +#, c-format +msgid "cannot delete %s: %s" +msgstr "không thể xoá bỏ %s: %s" + +#: dlltool.c:2811 +msgid "Created lib file" +msgstr "Đã tạo tập tin thư viên" + +#: dlltool.c:2904 +#, c-format +msgid "Warning, ignoring duplicate EXPORT %s %d,%d" +msgstr "Cảnh báo, đang bỏ qua XUẤT trùng %s %d,%d" + +#: dlltool.c:2910 +#, c-format +msgid "Error, duplicate EXPORT with oridinals: %s" +msgstr "Lỗi: XUẤT trùng với điều thứ tự : %s" + +#: dlltool.c:3026 +msgid "Processing definitions" +msgstr "Đang xử lý các lời định nghĩa..." + +#: dlltool.c:3058 +msgid "Processed definitions" +msgstr "Đã xử lý các lời định nghĩa" + +#. xgetext:c-format +#: dlltool.c:3065 dllwrap.c:479 +#, c-format +msgid "Usage %s \n" +msgstr "Cách sử dụng %s \n" + +#. xgetext:c-format +#: dlltool.c:3067 +#, c-format +msgid "" +" -m --machine Create as DLL for . [default: %s]\n" +msgstr " -m --machine Tạo dạng DLL cho . [mặc định: %s]\n" + +#: dlltool.c:3068 +#, c-format +msgid "" +" possible : arm[_interwork], i386, mcore[-elf]{-le|-be}, " +"ppc, thumb\n" +msgstr "" +" có thể: arm[_interwork], i386, mcore[-elf]{-le|-be}, ppc, " +"thumb\n" + +#: dlltool.c:3069 +#, c-format +msgid " -e --output-exp Generate an export file.\n" +msgstr " -e --output-exp \tTạo ra tập tin _xuất_.\n" + +#: dlltool.c:3070 +#, c-format +msgid " -l --output-lib Generate an interface library.\n" +msgstr " -l --output-lib \tTạo _ra thư viên_ giao diện.\n" + +#: dlltool.c:3071 +#, c-format +msgid " -a --add-indirect Add dll indirects to export file.\n" +msgstr "" +" -a --add-indirect _Thêm lời gián tiếp_dạng dll vào tập tin xuất\n" + +#: dlltool.c:3072 +#, c-format +msgid "" +" -D --dllname Name of input dll to put into interface lib.\n" +msgstr "" +" -D --dllname _Tên dll_ nhập cần để vào thư viên giao diện.\n" + +#: dlltool.c:3073 +#, c-format +msgid " -d --input-def Name of .def file to be read in.\n" +msgstr "" +" -d --input-def \tTên tập tin _định nghĩa_ cần đọc _vào_.\n" + +#: dlltool.c:3074 +#, c-format +msgid " -z --output-def Name of .def file to be created.\n" +msgstr "" +" -z --output-def Tên tập tin _định nghĩa_ cần tạo (_ra_).\n" + +#: dlltool.c:3075 +#, c-format +msgid " --export-all-symbols Export all symbols to .def\n" +msgstr "" +" --export-all-symbols Tự động _xuất mọi ký hiệu_ vào tập tin định nghĩa\n" + +#: dlltool.c:3076 +#, c-format +msgid " --no-export-all-symbols Only export listed symbols\n" +msgstr "" +" --no-export-all-symbols \tXuất chỉ những ký hiệu đã liệt kê\n" +"\t\t\t\t\t\t\t\t(_không xuất mọi ký hiệu_)\n" + +#: dlltool.c:3077 +#, c-format +msgid " --exclude-symbols Don't export \n" +msgstr "" +" --exclude-symbols Đừng xuất danh sách này\n" +"\t\t\t\t\t\t\t\t(_loại trừ ký hiệu_)\n" + +#: dlltool.c:3078 +#, c-format +msgid " --no-default-excludes Clear default exclude symbols\n" +msgstr "" +" --no-default-excludes Xoá các ký hiệu cần loại trừ theo mặc định\n" +"\t\t\t\t\t\t\t\t(không loại trừ mặc định)\n" + +#: dlltool.c:3079 +#, c-format +msgid " -b --base-file Read linker generated base file.\n" +msgstr "" +" -b --base-file Đọc _tập tin cơ bản_ do bộ liên kết tạo " +"ra.\n" + +#: dlltool.c:3080 +#, c-format +msgid " -x --no-idata4 Don't generate idata$4 section.\n" +msgstr " -x --no-idata4 Đừng tạo ra phần « idata$4 ».\n" + +#: dlltool.c:3081 +#, c-format +msgid " -c --no-idata5 Don't generate idata$5 section.\n" +msgstr " -c --no-idata5 Đừng tạo ra phần « idata$5 ».\n" + +#: dlltool.c:3082 +#, c-format +msgid "" +" -U --add-underscore Add underscores to symbols in interface " +"library.\n" +msgstr "" +" -U --add-underscore \t_Thêm dấu gạch dưới_ vào\n" +"\t\t\t\t\t\tcác ký hiệu trong thư viên giao diện.\n" + +#: dlltool.c:3083 +#, c-format +msgid " -k --kill-at Kill @ from exported names.\n" +msgstr "" +" -k --kill-at Xoá bỏ « @ » ra các tên đã xuất.\n" +"\t\t\t\t\t\t(_buộc kết thúc tại_)\n" + +#: dlltool.c:3084 +#, c-format +msgid " -A --add-stdcall-alias Add aliases without @.\n" +msgstr "" +" -A --add-stdcall-alias \tThêm biệt hiệu không có « @ ».\n" +"\t\t\t\t\t\t(_thêm biệt hiệu gọi chuẩn_)\n" + +#: dlltool.c:3085 +#, c-format +msgid " -p --ext-prefix-alias Add aliases with .\n" +msgstr "" +" -p --ext-prefix-alias \tThêm các biệt hiệu có tiền tố này.\n" +"\t\t\t\t\t\t(_biệt hiệu tiền tố thêm_)\n" + +#: dlltool.c:3086 +#, c-format +msgid " -S --as Use for assembler.\n" +msgstr "" +" -S --as \tDùng tên này cho chương trình dịch mã số.\n" + +#: dlltool.c:3087 +#, c-format +msgid " -f --as-flags Pass to the assembler.\n" +msgstr "" +" -f --as-flags Gởi các cờ này qua cho chương trình dịch mã " +"số.\n" +"\t\t\t\t\t\t\t(_dạng cờ_)\n" + +#: dlltool.c:3088 +#, c-format +msgid "" +" -C --compat-implib Create backward compatible import library.\n" +msgstr "" +" -C --compat-implib \tTạo _thư viên nhập tương thích_ ngược.\n" + +#: dlltool.c:3089 +#, c-format +msgid "" +" -n --no-delete Keep temp files (repeat for extra " +"preservation).\n" +msgstr "" +" -n --no-delete \t\tGiữ lại các tập tin tạm thời (lặp lại để bảo tồn " +"thêm)\n" +"\t\t\t\t\t\t(_không xoá bỏ_)\n" + +#: dlltool.c:3090 +#, c-format +msgid "" +" -t --temp-prefix Use to construct temp file names.\n" +msgstr "" +" -t --temp-prefix \tDùng _tiền tố_ này để tạo tên tập tin _tạm_.\n" + +#: dlltool.c:3091 +#, c-format +msgid " -v --verbose Be verbose.\n" +msgstr " -v --verbose Xuất _chi tiết_.\n" + +#: dlltool.c:3092 +#, c-format +msgid " -V --version Display the program version.\n" +msgstr " -V --version \tHiển thị phiên bản chương trình.\n" + +#: dlltool.c:3093 +#, c-format +msgid " -h --help Display this information.\n" +msgstr " -h --help \tHiển thị _trợ giúp_ này.\n" + +#: dlltool.c:3095 +#, c-format +msgid "" +" -M --mcore-elf Process mcore-elf object files into .\n" +msgstr "" +" -M --mcore-elf \n" +"\t\tXử lý các tập tin đối tượng kiểu « mcore-elf » vào tập tin tên này.\n" + +#: dlltool.c:3096 +#, c-format +msgid " -L --linker Use as the linker.\n" +msgstr " -L --linker \t\tDùng tên này là _bộ liên kết_.\n" + +#: dlltool.c:3097 +#, c-format +msgid " -F --linker-flags Pass to the linker.\n" +msgstr "" +" -F --linker-flags \tGởi _các cờ_ này qua cho _bộ liên kết_.\n" + +#: dlltool.c:3211 +#, c-format +msgid "Path components stripped from dllname, '%s'." +msgstr "Các thành phần đường dẫn bị tước ra tên dll, « %s »." + +#: dlltool.c:3256 +#, c-format +msgid "Unable to open base-file: %s" +msgstr "Không thể mở tập tin cơ sở: %s" + +#: dlltool.c:3288 +#, c-format +msgid "Machine '%s' not supported" +msgstr "Không hỗ trợ máy « %s »" + +#: dlltool.c:3392 dllwrap.c:209 +#, c-format +msgid "Tried file: %s" +msgstr "Đã thử tập tin: %s" + +#: dlltool.c:3399 dllwrap.c:216 +#, c-format +msgid "Using file: %s" +msgstr "Đang dùng tập tin: %s" + +#: dllwrap.c:299 +#, c-format +msgid "Keeping temporary base file %s" +msgstr "Đang giữ tập tin cơ bản tạm thời %s" + +#: dllwrap.c:301 +#, c-format +msgid "Deleting temporary base file %s" +msgstr "Đang xoá bỏ tập tin cơ bản tạm thời %s..." + +#: dllwrap.c:315 +#, c-format +msgid "Keeping temporary exp file %s" +msgstr "Đang giữ tập tin xuất tạm thời %s" + +#: dllwrap.c:317 +#, c-format +msgid "Deleting temporary exp file %s" +msgstr "Đang xoá bỏ tập tin xuất tạm thời %s..." + +#: dllwrap.c:330 +#, c-format +msgid "Keeping temporary def file %s" +msgstr "Đang giữ tập tin định nghĩa tạm thời %s" + +#: dllwrap.c:332 +#, c-format +msgid "Deleting temporary def file %s" +msgstr "Đang xoá bỏ tập tin định nghĩa tạm thời %s..." + +#: dllwrap.c:480 +#, c-format +msgid " Generic options:\n" +msgstr " Tùy chọn chung:\n" + +#: dllwrap.c:481 +#, c-format +msgid " --quiet, -q Work quietly\n" +msgstr " --quiet, -q Không xuất chi tiết (_im_)\n" + +#: dllwrap.c:482 +#, c-format +msgid " --verbose, -v Verbose\n" +msgstr " --verbose, -v Xuất _chi tiết_\n" + +#: dllwrap.c:483 +#, c-format +msgid " --version Print dllwrap version\n" +msgstr " --version In ra phiên bản dllwrap\n" + +#: dllwrap.c:484 +#, c-format +msgid " --implib Synonym for --output-lib\n" +msgstr " --implib Bằng « --output-lib »\n" + +#: dllwrap.c:485 +#, c-format +msgid " Options for %s:\n" +msgstr " Tùy chọn cho %s:\n" + +#: dllwrap.c:486 +#, c-format +msgid " --driver-name Defaults to \"gcc\"\n" +msgstr "" +" --driver-name \t Mặc định là « gcc »\n" +"\t\t\t\t\t\t\t\t(_tên trình điều khiển_)\n" + +#: dllwrap.c:487 +#, c-format +msgid " --driver-flags Override default ld flags\n" +msgstr "" +" --driver-flags \t\tCó quyền cao hơn các cờ ld mặc định\n" +"\t\t\t\t\t\t\t\t(_các cờ trình điều khiển_)\n" + +#: dllwrap.c:488 +#, c-format +msgid " --dlltool-name Defaults to \"dlltool\"\n" +msgstr "" +" --dlltool-name \t\tMặc định là « dlltool »\n" +"\t\t\t\t\t\t\t\t(_tên công cụ dlltool_)\n" + +#: dllwrap.c:489 +#, c-format +msgid " --entry Specify alternate DLL entry point\n" +msgstr " --entry <điểm_vào> \t\tGhi rõ điểm _vào_ DLL xen kẽ\n" + +#: dllwrap.c:490 +#, c-format +msgid " --image-base Specify image base address\n" +msgstr " --image-base \tGhi rõ địa chỉ _cơ bản ảnh_\n" + +#: dllwrap.c:491 +#, c-format +msgid " --target i386-cygwin32 or i386-mingw32\n" +msgstr " --target i386-cygwin32 hay i386-mingw32\n" + +#: dllwrap.c:492 +#, c-format +msgid " --dry-run Show what needs to be run\n" +msgstr "" +" --dry-run \tHiển thị các điều cần chạy (_chạy thực hành_)\n" + +#: dllwrap.c:493 +#, c-format +msgid " --mno-cygwin Create Mingw DLL\n" +msgstr " --mno-cygwin \tTạo DLL dạng Mingw\n" + +#: dllwrap.c:494 +#, c-format +msgid " Options passed to DLLTOOL:\n" +msgstr " Các tùy chọn được gởi qua cho DLLTOOL:\n" + +#: dllwrap.c:495 +#, c-format +msgid " --machine \n" +msgstr " --machine \n" + +#: dllwrap.c:496 +#, c-format +msgid " --output-exp Generate export file.\n" +msgstr " --output-exp \t\tTạo ra tập tin _xuất_.\n" + +#: dllwrap.c:497 +#, c-format +msgid " --output-lib Generate input library.\n" +msgstr " --output-lib \t\tTạo _ra thư viên_ nhập.\n" + +#: dllwrap.c:498 +#, c-format +msgid " --add-indirect Add dll indirects to export file.\n" +msgstr "" +" --add-indirect \t\t_Thêm_ các lời _gián tiếp_ vào tập tin xuất.\n" + +#: dllwrap.c:499 +#, c-format +msgid " --dllname Name of input dll to put into output lib.\n" +msgstr "" +" --dllname \t\t_Tên dll_ nhập cần để vào thư viên xuất.\n" + +#: dllwrap.c:500 +#, c-format +msgid " --def Name input .def file\n" +msgstr " --def \tTên tập tin _định nghĩa_ nhập\n" + +#: dllwrap.c:501 +#, c-format +msgid " --output-def Name output .def file\n" +msgstr " --output-def \tTên _tập tin định nghĩa xuất_\n" + +#: dllwrap.c:502 +#, c-format +msgid " --export-all-symbols Export all symbols to .def\n" +msgstr "" +" --export-all-symbols _Xuất mọi ký hiệu_ vào tập tin định nghĩa\n" + +#: dllwrap.c:503 +#, c-format +msgid " --no-export-all-symbols Only export .drectve symbols\n" +msgstr "" +" --no-export-all-symbols \tXuất chỉ ký hiệu kiểu « .drectve ».\n" +"\t\t\t\t\t\t\t\t(_không xuất mọi ký hiệu_)\n" + +#: dllwrap.c:504 +#, c-format +msgid " --exclude-symbols Exclude from .def\n" +msgstr "" +" --exclude-symbols \n" +"\t\t\t\t\tLoại trừ danh sách này ra tập tin định nghĩa.\n" +"\t\t\t\t\t\t\t\t(_loại trừ các ký hiệu_)\n" + +#: dllwrap.c:505 +#, c-format +msgid " --no-default-excludes Zap default exclude symbols\n" +msgstr "" +" --no-default-excludes \t\tSửa mọi ký hiệu loại trừ mặc định.\n" +"\t\t\t\t\t\t\t\t(_không loại trừ mặc định_)\n" + +#: dllwrap.c:506 +#, c-format +msgid " --base-file Read linker generated base file\n" +msgstr "" +" --base-file Đọc _tập tin cơ bản_ do bộ liên kết tạo " +"ra.\n" + +#: dllwrap.c:507 +#, c-format +msgid " --no-idata4 Don't generate idata$4 section\n" +msgstr " --no-idata4 Đừng tạo ra phần « idata$4 ».\n" + +#: dllwrap.c:508 +#, c-format +msgid " --no-idata5 Don't generate idata$5 section\n" +msgstr " --no-idata5 Đừng tạo ra phần « idata$5 ».\n" + +#: dllwrap.c:509 +#, c-format +msgid " -U Add underscores to .lib\n" +msgstr " -U Thêm dấu gạch _dưới_ vào thư viên\n" + +#: dllwrap.c:510 +#, c-format +msgid " -k Kill @ from exported names\n" +msgstr "" +" -k Xoá bỏ « @ » ra các tên đã xuất\n" +"\t\t\t\t\t(_buộc kết thúc_)\n" + +#: dllwrap.c:511 +#, c-format +msgid " --add-stdcall-alias Add aliases without @\n" +msgstr "" +" --add-stdcall-alias \tThêm biệt hiệu không có « @ ».\n" +"\t\t\t\t\t\t\t(_thêm biệt hiệu gọi chuẩn_)\n" + +#: dllwrap.c:512 +#, c-format +msgid " --as Use for assembler\n" +msgstr "" +" --as Dùng tên này cho chương trình dịch mã số (_dạng_)\n" + +#: dllwrap.c:513 +#, c-format +msgid " --nodelete Keep temp files.\n" +msgstr " --nodelete Giữ các tập tin tạm (_không xoá bỏ_)\n" + +#: dllwrap.c:514 +#, c-format +msgid " Rest are passed unmodified to the language driver\n" +msgstr "" +" Các điều còn lại được gởi dạng chưa được sửa đổi qua cho trình điều khiển " +"ngôn ngữ\n" + +#: dllwrap.c:784 +msgid "Must provide at least one of -o or --dllname options" +msgstr "Phải cung cấp ít nhất một của hai tùy chọn « -o » hay « -dllname »" + +#: dllwrap.c:813 +msgid "" +"no export definition file provided.\n" +"Creating one, but that may not be what you want" +msgstr "" +"chưa cung cấp tập tin định nghĩa xuất.\n" +"Đang tạo một điều, mà có lẽ không phải là điều bạn muốn" + +#: dllwrap.c:972 +#, c-format +msgid "DLLTOOL name : %s\n" +msgstr "Tên công cụ DLLTOOL : %s\n" + +#: dllwrap.c:973 +#, c-format +msgid "DLLTOOL options : %s\n" +msgstr "Tùy chọn DLLTOOL: %s\n" + +#: dllwrap.c:974 +#, c-format +msgid "DRIVER name : %s\n" +msgstr "Tên TRÌNH ĐIỀU KHIỀN : %s\n" + +#: dllwrap.c:975 +#, c-format +msgid "DRIVER options : %s\n" +msgstr "Tùy chọn TRÌNH ĐIỀU KHIỂN : %s\n" + +#: emul_aix.c:51 +#, c-format +msgid " [-g] - 32 bit small archive\n" +msgstr " [-g] • kho nhỏ 32-bit\n" + +#: emul_aix.c:52 +#, c-format +msgid " [-X32] - ignores 64 bit objects\n" +msgstr " [-X32] • bỏ qua các đối tượng kiểu 64 bit\n" + +#: emul_aix.c:53 +#, c-format +msgid " [-X64] - ignores 32 bit objects\n" +msgstr " [-X64] • bỏ qua các đối tượng kiểu 32 bit\n" + +#: emul_aix.c:54 +#, c-format +msgid " [-X32_64] - accepts 32 and 64 bit objects\n" +msgstr "" +" [-X32_64] • chấp nhận các đối tượng kiểu cả 32 bit lẫn 64 bit đều\n" + +#: ieee.c:311 +msgid "unexpected end of debugging information" +msgstr "gặp kết thúc thông tin gỡ lỗi bất ngờ" + +#: ieee.c:398 +msgid "invalid number" +msgstr "số không hợp lệ" + +#: ieee.c:451 +msgid "invalid string length" +msgstr "độ dài chuỗi không hợp lệ" + +#: ieee.c:506 ieee.c:547 +msgid "expression stack overflow" +msgstr "trán đống biểu thức" + +#: ieee.c:526 +msgid "unsupported IEEE expression operator" +msgstr "toán tử biểu thức IEE không được hỗ trợ" + +#: ieee.c:541 +msgid "unknown section" +msgstr "không biết phần" + +#: ieee.c:562 +msgid "expression stack underflow" +msgstr "trán ngược đống biểu thức" + +#: ieee.c:576 +msgid "expression stack mismatch" +msgstr "đống biểu thức không khớp với nhau" + +#: ieee.c:613 +msgid "unknown builtin type" +msgstr "không biết kiểu builtin" + +#: ieee.c:758 +msgid "BCD float type not supported" +msgstr "Kiểu nổi BDC không được hỗ trợ" + +#: ieee.c:895 +msgid "unexpected number" +msgstr "số bất ngờ" + +#: ieee.c:902 +msgid "unexpected record type" +msgstr "kiểu mục ghi bất ngờ" + +#: ieee.c:935 +msgid "blocks left on stack at end" +msgstr "có một số khối còn lại trên đống khi kết thúc" + +#: ieee.c:1198 +msgid "unknown BB type" +msgstr "không biết kiểu BB" + +#: ieee.c:1207 lib/c-stack.c:245 +msgid "stack overflow" +msgstr "trán đống" + +#: ieee.c:1230 +msgid "stack underflow" +msgstr "trán ngược đống" + +#: ieee.c:1342 ieee.c:1412 ieee.c:2109 +msgid "illegal variable index" +msgstr "chỉ mục biến không được phép" + +#: ieee.c:1390 +msgid "illegal type index" +msgstr "chỉ mục kiểu không được phép" + +#: ieee.c:1400 ieee.c:1437 +msgid "unknown TY code" +msgstr "không biết mã TY" + +#: ieee.c:1419 +msgid "undefined variable in TY" +msgstr "gặp biến chưa được định nghĩa trong TY" + +#. Pascal file name. FIXME. +#: ieee.c:1830 +msgid "Pascal file name not supported" +msgstr "Chưa hỗ trợ tên tập tin kiểu Pascal" + +#: ieee.c:1878 +msgid "unsupported qualifier" +msgstr "bộ dè dặt chưa được hỗ trợ" + +#: ieee.c:2147 +msgid "undefined variable in ATN" +msgstr "gặp biến chưa định nghĩa trong ATN" + +#: ieee.c:2190 +msgid "unknown ATN type" +msgstr "không biết kiểu ATN" + +#. Reserved for FORTRAN common. +#: ieee.c:2312 +msgid "unsupported ATN11" +msgstr "ATN11 không được hỗ trơ" + +#. We have no way to record this information. FIXME. +#: ieee.c:2339 +msgid "unsupported ATN12" +msgstr "ATN12 không được hỗ trơ" + +#: ieee.c:2399 +msgid "unexpected string in C++ misc" +msgstr "gặp chuỗi không được hỗ trơ trong C++ lặt vặt" + +#: ieee.c:2412 +msgid "bad misc record" +msgstr "mục ghi lặt vặt sai" + +#: ieee.c:2453 +msgid "unrecognized C++ misc record" +msgstr "không chấp nhận mục ghi C++ lặt vặt" + +#: ieee.c:2568 +msgid "undefined C++ object" +msgstr "đối tượng C++ chưa được định nghĩa" + +#: ieee.c:2602 +msgid "unrecognized C++ object spec" +msgstr "chưa chấp nhận đặc tả đối tượng C++" + +#: ieee.c:2638 +msgid "unsupported C++ object type" +msgstr "kiểu đối tượng C++ chưa được hỗ trợ" + +#: ieee.c:2648 +msgid "C++ base class not defined" +msgstr "chưa định nghĩa hạng cơ bản C++" + +#: ieee.c:2660 ieee.c:2765 +msgid "C++ object has no fields" +msgstr "Đối tượng C++ không có trường nào" + +#: ieee.c:2679 +msgid "C++ base class not found in container" +msgstr "Không tìm thấy hạng cơ bản C++ trong bộ chứa" + +#: ieee.c:2786 +msgid "C++ data member not found in container" +msgstr "Không tìm thấy bộ phạn dữ liệu C++ trong bộ chứa" + +#: ieee.c:2827 ieee.c:2977 +msgid "unknown C++ visibility" +msgstr "không biết độ thấy rõ C++" + +#: ieee.c:2861 +msgid "bad C++ field bit pos or size" +msgstr "vị trí bit hay kích cỡ trường C++ sai" + +#: ieee.c:2953 +msgid "bad type for C++ method function" +msgstr "kiểu sai cho hàm phương pháp C++" + +#: ieee.c:2963 +msgid "no type information for C++ method function" +msgstr "không có thông tin kiểu cho hàm phương pháp C++" + +#: ieee.c:3002 +msgid "C++ static virtual method" +msgstr "phương pháp ảo tĩnh C++" + +#: ieee.c:3097 +msgid "unrecognized C++ object overhead spec" +msgstr "chưa chấp nhận đặc tả duy tu đối tượng C++" + +#: ieee.c:3136 +msgid "undefined C++ vtable" +msgstr "chưa định nghĩa vtable C++" + +#: ieee.c:3205 +msgid "C++ default values not in a function" +msgstr "Giá trị C++ mặc định không phải trong hàm" + +#: ieee.c:3245 +msgid "unrecognized C++ default type" +msgstr "chưa chấp nhận kiểu C++ mặc định" + +#: ieee.c:3276 +msgid "reference parameter is not a pointer" +msgstr "tham số tham chiếu không phải là con trỏ" + +#: ieee.c:3359 +msgid "unrecognized C++ reference type" +msgstr "chưa chấp nhận kiểu tham chiếu C++" + +#: ieee.c:3441 +msgid "C++ reference not found" +msgstr "Không tìm thấy tham chiếu C++" + +#: ieee.c:3449 +msgid "C++ reference is not pointer" +msgstr "Tham chiếu C++ không phải là con trỏ" + +#: ieee.c:3475 ieee.c:3483 +msgid "missing required ASN" +msgstr "thiếu ASN cần thiết" + +#: ieee.c:3510 ieee.c:3518 +msgid "missing required ATN65" +msgstr "thiếu ATN65 cần thiết" + +#: ieee.c:3532 +msgid "bad ATN65 record" +msgstr "mục ghi ATN65 sai" + +#: ieee.c:4160 +#, c-format +msgid "IEEE numeric overflow: 0x" +msgstr "trán thuộc số IEEE: 0x" + +#: ieee.c:4204 +#, c-format +msgid "IEEE string length overflow: %u\n" +msgstr "Trán độ dài chuỗi IEEE: %u\n" + +#: ieee.c:5203 +#, c-format +msgid "IEEE unsupported integer type size %u\n" +msgstr "Kích cỡ kiểu số nguyên không được hỗ trợ IEEE %u\n" + +#: ieee.c:5237 +#, c-format +msgid "IEEE unsupported float type size %u\n" +msgstr "Kích cỡ kiểu nổi không được hỗ trợ IEEE %u\n" + +#: ieee.c:5271 +#, c-format +msgid "IEEE unsupported complex type size %u\n" +msgstr "Kích cỡ kiểu phức tạp không được hỗ trợ IEEE %u\n" + +#: nlmconv.c:267 srconv.c:1810 +msgid "input and output files must be different" +msgstr "tập tin nhập và xuất phải là khác nhau" + +#: nlmconv.c:314 +msgid "input file named both on command line and with INPUT" +msgstr "tên tập tin được lập cả trên dòng lệnh lẫn bằng INPUT đều" + +#: nlmconv.c:323 +msgid "no input file" +msgstr "không có tập tin nhập nào" + +#: nlmconv.c:353 +msgid "no name for output file" +msgstr "không có tên cho tập tin nhập" + +#: nlmconv.c:367 +msgid "warning: input and output formats are not compatible" +msgstr "cảnh báo : khuôn dạng nhập và xuất không tương thích với nhau" + +#: nlmconv.c:396 +msgid "make .bss section" +msgstr "tạo phần « .bss »" + +#: nlmconv.c:405 +msgid "make .nlmsections section" +msgstr "tạo phần « .nlmsections »" + +#: nlmconv.c:407 +msgid "set .nlmsections flags" +msgstr "đặt các cờ « .nlmsections »" + +#: nlmconv.c:435 +msgid "set .bss vma" +msgstr "đặt vma .bss" + +#: nlmconv.c:442 +msgid "set .data size" +msgstr "đặt kích cỡ dữ liệu .data" + +#: nlmconv.c:622 +#, c-format +msgid "warning: symbol %s imported but not in import list" +msgstr "cảnh báo : ký hiệu %s được nhập mà không phải trong danh sách nhập" + +#: nlmconv.c:642 +msgid "set start address" +msgstr "đặt địa chỉ bắt đầu" + +#: nlmconv.c:691 +#, c-format +msgid "warning: START procedure %s not defined" +msgstr "cảnh báo : thủ tục START (bắt đầu) %s chưa được định nghĩa" + +#: nlmconv.c:693 +#, c-format +msgid "warning: EXIT procedure %s not defined" +msgstr "cảnh báo : thủ tục EXIT (thoát) %s chưa được định nghĩa" + +#: nlmconv.c:695 +#, c-format +msgid "warning: CHECK procedure %s not defined" +msgstr "cảnh báo : thủ tục CHECK (kiểm tra) %s chưa được định nghĩa" + +#: nlmconv.c:716 nlmconv.c:905 +msgid "custom section" +msgstr "phần tự chọn" + +#: nlmconv.c:737 nlmconv.c:934 +msgid "help section" +msgstr "phần trợ giúp" + +#: nlmconv.c:759 nlmconv.c:952 +msgid "message section" +msgstr "phần thông điệp" + +#: nlmconv.c:775 nlmconv.c:985 +msgid "module section" +msgstr "phần mô-đun" + +#: nlmconv.c:795 nlmconv.c:1001 +msgid "rpc section" +msgstr "phần rpc" + +#. There is no place to record this information. +#: nlmconv.c:831 +#, c-format +msgid "%s: warning: shared libraries can not have uninitialized data" +msgstr "" +"%s: cảnh báo : thư viên dùng chung không thể chứa dữ liệu chưa được sở khởi" + +#: nlmconv.c:852 nlmconv.c:1020 +msgid "shared section" +msgstr "phần dùng chung" + +#: nlmconv.c:860 +msgid "warning: No version number given" +msgstr "cảnh báo : chưa đưa ra số thứ tự phiên bản" + +#: nlmconv.c:900 nlmconv.c:929 nlmconv.c:947 nlmconv.c:996 nlmconv.c:1015 +#, c-format +msgid "%s: read: %s" +msgstr "%s: đọc: %s" + +#: nlmconv.c:922 +msgid "warning: FULLMAP is not supported; try ld -M" +msgstr "cảnh báo : chưa hỗ trợ FULLMAP; hãy thử « ld -M »" + +#: nlmconv.c:1098 +#, c-format +msgid "Usage: %s [option(s)] [in-file [out-file]]\n" +msgstr "Cách sử dụng: %s [tùy_chọn...] [tập_tin_nhập [tập_tin_xuất]]\n" + +#: nlmconv.c:1099 +#, c-format +msgid " Convert an object file into a NetWare Loadable Module\n" +msgstr "" +" Chuyển đổi tập tin đối tượng sang Mô-đun Tải được NetWare (NetWare Loadable " +"Module)\n" + +#: nlmconv.c:1100 +#, c-format +msgid "" +" The options are:\n" +" -I --input-target= Set the input binary file format\n" +" -O --output-target= Set the output binary file format\n" +" -T --header-file= Read for NLM header information\n" +" -l --linker= Use for any linking\n" +" -d --debug Display on stderr the linker command line\n" +" -h --help Display this information\n" +" -v --version Display the program's version\n" +msgstr "" +" Tùy chọn:\n" +" -I --input-target= \t\tLập dạng thức tập tin nhị phân nhập\n" +"\t\t\t\t\t\t\t\t (_đích nhập_)\n" +" -O --output-target= \tLập dạng thức tập tin nhị phân xuất\n" +"\t\t\t\t\t\t\t\t (_đích xuất_)\n" +" -T --header-file=\n" +"\t\tĐọc tập tin này để tìm thông tin phần đầu NLM (_tập tin phần đầu_)\n" +" -l --linker= \tDùng _bộ liên kết_ này khi liên kết\n" +" -d --debug\n" +"\tHiển thị trên thiết bị lỗi chuẩn dòng lệnh của bộ liên kết (_gỡ lỗi_)\n" +" -h --help \t\t\tHiển thị _trợ giúp_ này\n" +" -v --version \t\t\tHiển thị _phiên bản_ chương trình\n" + +#: nlmconv.c:1140 +#, c-format +msgid "support not compiled in for %s" +msgstr "chưa biên dịch cách hỗ trợ %s" + +#: nlmconv.c:1177 +msgid "make section" +msgstr "tạo phần" + +#: nlmconv.c:1191 +msgid "set section size" +msgstr "lập kích cỡ phần" + +#: nlmconv.c:1197 +msgid "set section alignment" +msgstr "lập canh lề phần" + +#: nlmconv.c:1201 +msgid "set section flags" +msgstr "lập các cờ phân" + +#: nlmconv.c:1212 +msgid "set .nlmsections size" +msgstr "lập kích cỡ « .nlmsections »" + +#: nlmconv.c:1293 nlmconv.c:1301 nlmconv.c:1310 nlmconv.c:1315 +msgid "set .nlmsection contents" +msgstr "lập nội dung « .nlmsections »" + +#: nlmconv.c:1794 +msgid "stub section sizes" +msgstr "kích cỡ phần stub" + +#: nlmconv.c:1841 +msgid "writing stub" +msgstr "đang ghi stub..." + +#: nlmconv.c:1925 +#, c-format +msgid "unresolved PC relative reloc against %s" +msgstr "có việc định vị lại liên quan đến PC chưa tháo gỡ đối với %s" + +#: nlmconv.c:1989 +#, c-format +msgid "overflow when adjusting relocation against %s" +msgstr "trán khi điều chỉnh việc định vị lại đối với %s" + +#: nlmconv.c:2116 +#, c-format +msgid "%s: execution of %s failed: " +msgstr "%s: việc thực hiện %s bị lỗi:" + +#: nlmconv.c:2131 ../applet/pilot.c:1095 +#, c-format +msgid "Execution of %s failed" +msgstr "Việc thực hiện %s bị lỗi" + +#: nm.c:224 size.c:80 strings.c:651 +#, c-format +msgid "Usage: %s [option(s)] [file(s)]\n" +msgstr "Cách sử dụng: %s [tùy_chọn...] [tập_tin...]\n" + +#: nm.c:225 +#, c-format +msgid " List symbols in [file(s)] (a.out by default).\n" +msgstr " Liệt kê các ký hiệu trong những tập tin này (mặc định là ).\n" + +#: nm.c:226 +#, c-format +msgid "" +" The options are:\n" +" -a, --debug-syms Display debugger-only symbols\n" +" -A, --print-file-name Print name of the input file before every symbol\n" +" -B Same as --format=bsd\n" +" -C, --demangle[=STYLE] Decode low-level symbol names into user-level " +"names\n" +" The STYLE, if specified, can be `auto' (the " +"default),\n" +" `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', " +"`java'\n" +" or `gnat'\n" +" --no-demangle Do not demangle low-level symbol names\n" +" -D, --dynamic Display dynamic symbols instead of normal symbols\n" +" --defined-only Display only defined symbols\n" +" -e (ignored)\n" +" -f, --format=FORMAT Use the output format FORMAT. FORMAT can be " +"`bsd',\n" +" `sysv' or `posix'. The default is `bsd'\n" +" -g, --extern-only Display only external symbols\n" +" -l, --line-numbers Use debugging information to find a filename and\n" +" line number for each symbol\n" +" -n, --numeric-sort Sort symbols numerically by address\n" +" -o Same as -A\n" +" -p, --no-sort Do not sort the symbols\n" +" -P, --portability Same as --format=posix\n" +" -r, --reverse-sort Reverse the sense of the sort\n" +" -S, --print-size Print size of defined symbols\n" +" -s, --print-armap Include index for symbols from archive members\n" +" --size-sort Sort symbols by size\n" +" --special-syms Include special symbols in the output\n" +" --synthetic Display synthetic symbols as well\n" +" -t, --radix=RADIX Use RADIX for printing symbol values\n" +" --target=BFDNAME Specify the target object format as BFDNAME\n" +" -u, --undefined-only Display only undefined symbols\n" +" -X 32_64 (ignored)\n" +" -h, --help Display this information\n" +" -V, --version Display this program's version number\n" +"\n" +msgstr "" +" Tùy chọn:\n" +" -a, --debug-syms \tHiển thị _ký hiệu_ chỉ kiểu bộ _gỡ lỗi_ thôi\n" +" -A, --print-file-name \t_In ra tên tập tin_ nhập trước mỗi ký hiệu\n" +" -B \t\t\tBằng « --format=bsd »\n" +" -C, --demangle[=KIỂU_DÁNG]\n" +"\tGiải mã các tên ký hiệu cấp thấp thành tên cấp người dùng (_tháo gỡ_)\n" +" Kiểu dáng này, nếu được ghi rõ, có thể là « auto » (tự động: mặc " +"định)\n" +"\t« gnu », « lucid », « arm », « hp », « edg », « gnu-v3 », « java » hay « " +"gnat ».\n" +" --no-demangle \t\t_Đừng tháo gỡ_ tên ký hiệu cấp thấp\n" +" -D, --dynamic \t\tHiển thị ký hiệu _động_ thay vào ký hiệu chuẩn\n" +" --defined-only \t\tHiển thị _chỉ_ ký hiệu _được định nghĩa_\n" +" -e \t\t\t(bị bỏ qua)\n" +" -f, --format=DẠNG_THỨC \tDùng _dạng thức_ xuất này, một của\n" +"\t\t\t\t\t\t\t« bsd » (mặc định), « sysv » hay « posix »\n" +" -g, --extern-only \t\tHiển thị _chỉ_ ký hiệu _bên ngoài_\n" +" -l, --line-numbers \t\tDùng thông tin gỡ lỗi để tìm tên tập tin\n" +"\t\t\t\t\t\tvà _số thứ tự dòng_ cho mỗi ký hiệu\n" +" -n, --numeric-sort \t\t_Sắp xếp_ ký hiệu một cách _thuộc số_ theo địa " +"chỉ\n" +" -o \t\t\tBằng « -A »\n" +" -p, --no-sort \t\t_Đừng sắp xếp_ ký hiệu\n" +" -P, --portability \t\tBằng « --format=posix »\n" +" -r, --reverse-sort \t\t_Sắp xếp ngược_\n" +" -S, --print-size \t\tIn ra kích cỡ của các ký hiệu được định nghĩa\n" +" -s, --print-armap \t\tGồm chỉ mục cho ký hiệu từ bộ phạn kho\n" +" --size-sort \t\t_Sắp xếp_ ký hiệu theo _kích cỡ_\n" +" --special-syms \t\tGồm _ký hiệu đặc biệt_ trong dữ liệu xuất\n" +" --synthetic \t\tCũng hiển thị ký hiệu _tổng hợp_\n" +" -t, --radix=CƠ_SỞ \tDùng _cơ sở_ này để in ra giá trị ký hiệu\n" +" --target=TÊN_BFD \tGhi rõ dạng thức đối tượng _đích_ là tên BFD này\n" +" -u, --undefined-only \tHiển thị _chỉ_ ký hiệu _chưa được định nghĩa_\n" +" -X 32_64 \t\t(bị bỏ qua)\n" +" -h, --help \t\tHiển thị _trợ giúp_ này\n" +" -V, --version \t\tHiển thị số thứ tự _phiên bản_ của chương trình " +"này\n" +"\n" + +#: nm.c:262 objdump.c:232 lib/argp-help.c:1653 lib/argp-help.c:1652 +#, c-format +msgid "Report bugs to %s.\n" +msgstr "Hãy thông báo lỗi nào cho %s.\n" + +#: nm.c:294 +#, c-format +msgid "%s: invalid radix" +msgstr "%s: cơ sở không hợp lệ" + +#: nm.c:318 +#, c-format +msgid "%s: invalid output format" +msgstr "%s: dạng thức xuất không hợp lệ" + +#: nm.c:339 readelf.c:6342 readelf.c:6378 +#, c-format +msgid ": %d" +msgstr "<đặc trưng cho bộ xử lý>: %d" + +#: nm.c:341 readelf.c:6345 readelf.c:6390 +#, c-format +msgid ": %d" +msgstr "<đặc trưng cho hệ điều hành>: %d" + +#: nm.c:343 readelf.c:6347 readelf.c:6393 +#, c-format +msgid ": %d" +msgstr ": %d" + +#: nm.c:380 +#, c-format +msgid "" +"\n" +"Archive index:\n" +msgstr "" +"\n" +"Chỉ mục kho:\n" + +#: nm.c:1225 +#, c-format +msgid "" +"\n" +"\n" +"Undefined symbols from %s:\n" +"\n" +msgstr "" +"\n" +"\n" +"Ký hiệu chưa được định nghĩa từ %s:\n" +"\n" + +#: nm.c:1227 +#, c-format +msgid "" +"\n" +"\n" +"Symbols from %s:\n" +"\n" +msgstr "" +"\n" +"\n" +"Ký hiệu từ %s:\n" +"\n" + +#: nm.c:1229 nm.c:1280 +#, c-format +msgid "" +"Name Value Class Type Size Line " +"Section\n" +"\n" +msgstr "" +"Tên Giá trị Hạng Kiểu Cỡ Dòng Phần\n" +"\n" + +#: nm.c:1232 nm.c:1283 +#, c-format +msgid "" +"Name Value Class Type " +"Size Line Section\n" +"\n" +msgstr "" +"Name Value Class Type " +"Size Line Section\n" +"\n" + +#: nm.c:1276 +#, c-format +msgid "" +"\n" +"\n" +"Undefined symbols from %s[%s]:\n" +"\n" +msgstr "" +"\n" +"\n" +"Ký hiệu chưa được định nghĩa từUndefined symbols from %s[%s]:\n" +"\n" + +#: nm.c:1278 +#, c-format +msgid "" +"\n" +"\n" +"Symbols from %s[%s]:\n" +"\n" +msgstr "" +"\n" +"\n" +"Ký hiệu từ %s[%s]:\n" +"\n" + +#: nm.c:1580 +msgid "Only -X 32_64 is supported" +msgstr "Chỉ hỗ trợ « -X 32_64 »" + +#: nm.c:1600 +msgid "Using the --size-sort and --undefined-only options together" +msgstr "Dùng tùy chọn cả « --size-sort » lẫn « --undefined-only » đều" + +#: nm.c:1601 +msgid "will produce no output, since undefined symbols have no size." +msgstr "" +"sẽ không xuất gì, vì ký hiệu chưa được định nghĩa không có kích cỡ nào." + +#: nm.c:1629 +#, c-format +msgid "data size %ld" +msgstr "cỡ dữ liệu %ld" + +#: objcopy.c:396 srconv.c:1721 +#, c-format +msgid "Usage: %s [option(s)] in-file [out-file]\n" +msgstr "Cách sử dụng: %s [tùy_chọn...] tập_tin_nhập [tập_tin_xuất]\n" + +#: objcopy.c:397 +#, c-format +msgid " Copies a binary file, possibly transforming it in the process\n" +msgstr " Sao chép tập tin nhị phân, cũng có thể chuyển đổi nó\n" + +#: objcopy.c:398 objcopy.c:487 +#, c-format +msgid " The options are:\n" +msgstr " Tùy chọn:\n" + +#: objcopy.c:399 +#, c-format +msgid "" +" -I --input-target Assume input file is in format \n" +" -O --output-target Create an output file in format " +"\n" +" -B --binary-architecture Set arch of output file, when input is " +"binary\n" +" -F --target Set both input and output format to " +"\n" +" --debugging Convert debugging information, if " +"possible\n" +" -p --preserve-dates Copy modified/access timestamps to the " +"output\n" +" -j --only-section Only copy section into the output\n" +" --add-gnu-debuglink= Add section .gnu_debuglink linking to " +"\n" +" -R --remove-section Remove section from the output\n" +" -S --strip-all Remove all symbol and relocation " +"information\n" +" -g --strip-debug Remove all debugging symbols & sections\n" +" --strip-unneeded Remove all symbols not needed by " +"relocations\n" +" -N --strip-symbol Do not copy symbol \n" +" --strip-unneeded-symbol \n" +" Do not copy symbol unless needed " +"by\n" +" relocations\n" +" --only-keep-debug Strip everything but the debug " +"information\n" +" -K --keep-symbol Only copy symbol \n" +" -L --localize-symbol Force symbol to be marked as a " +"local\n" +" -G --keep-global-symbol Localize all symbols except \n" +" -W --weaken-symbol Force symbol to be marked as a " +"weak\n" +" --weaken Force all global symbols to be marked as " +"weak\n" +" -w --wildcard Permit wildcard in symbol comparison\n" +" -x --discard-all Remove all non-global symbols\n" +" -X --discard-locals Remove any compiler-generated symbols\n" +" -i --interleave Only copy one out of every " +"bytes\n" +" -b --byte Select byte in every interleaved " +"block\n" +" --gap-fill Fill gaps between sections with \n" +" --pad-to Pad the last section up to address " +"\n" +" --set-start Set the start address to \n" +" {--change-start|--adjust-start} \n" +" Add to the start address\n" +" {--change-addresses|--adjust-vma} \n" +" Add to LMA, VMA and start " +"addresses\n" +" {--change-section-address|--adjust-section-vma} {=|+|-}\n" +" Change LMA and VMA of section by " +"\n" +" --change-section-lma {=|+|-}\n" +" Change the LMA of section by " +"\n" +" --change-section-vma {=|+|-}\n" +" Change the VMA of section by " +"\n" +" {--[no-]change-warnings|--[no-]adjust-warnings}\n" +" Warn if a named section does not exist\n" +" --set-section-flags =\n" +" Set section 's properties to " +"\n" +" --add-section = Add section found in to " +"output\n" +" --rename-section =[,] Rename section to \n" +" --change-leading-char Force output format's leading character " +"style\n" +" --remove-leading-char Remove leading character from global " +"symbols\n" +" --redefine-sym = Redefine symbol name to \n" +" --redefine-syms --redefine-sym for all symbol pairs \n" +" listed in \n" +" --srec-len Restrict the length of generated " +"Srecords\n" +" --srec-forceS3 Restrict the type of generated Srecords " +"to S3\n" +" --strip-symbols -N for all symbols listed in \n" +" --strip-unneeded-symbols \n" +" --strip-unneeded-symbol for all symbols " +"listed\n" +" in \n" +" --keep-symbols -K for all symbols listed in \n" +" --localize-symbols -L for all symbols listed in \n" +" --keep-global-symbols -G for all symbols listed in \n" +" --weaken-symbols -W for all symbols listed in \n" +" --alt-machine-code Use alternate machine code for output\n" +" --writable-text Mark the output text as writable\n" +" --readonly-text Make the output text write protected\n" +" --pure Mark the output file as demand paged\n" +" --impure Mark the output file as impure\n" +" --prefix-symbols Add to start of every symbol " +"name\n" +" --prefix-sections Add to start of every section " +"name\n" +" --prefix-alloc-sections \n" +" Add to start of every " +"allocatable\n" +" section name\n" +" -v --verbose List all object files modified\n" +" -V --version Display this program's version number\n" +" -h --help Display this output\n" +" --info List object formats & architectures " +"supported\n" +msgstr "" +" -I --input-target \t\tGiả sử tập tin nhập có dạng \n" +"\t\t\t\t\t\t\t\t (_đích nhập_)\n" +" -O --output-target \tTạo tập tin dạng \n" +"\t\t\t\t\t\t\t\t (_đích xuất_)\n" +" -B --binary-architecture \n" +"\t\t\tLập _kiến trúc_ của tập tin xuất, khi tập tin nhập là _nhị phân_\n" +" -F --target \n" +"\t\t\tLập dạng thức cả nhập lẫn xuất đều thành (_đích_)\n" +" --debugging \t\t\tChuyển đổi thông tin _gỡ lỗi_, nếu " +"có thể\n" +" -p --preserve-dates\n" +"\tSao chép nhãn thời gian truy cập/sửa đổi vào kết xuất (_bảo tồn các " +"ngày_)\n" +" -j --only-section \t_Chỉ_ sao chép _phần_ vào kết " +"xuất\n" +" --add-gnu-debuglink=\n" +"\t\t_Thêm_ khả năng liên kết phần « .gnu_debuglink » vào \n" +" -R --remove-section \t_Gỡ bỏ phần_ ra kết xuất\n" +" -S --strip-all \t\t\tGỡ bỏ mọi thông tin ký hiệu và định " +"vị lại\n" +"\t\t\t\t\t\t\t\t (_tước hết_)\n" +" -g --strip-debug \t\tGỡ bỏ mọi ký hiệu và phần kiểu gỡ " +"lỗi\n" +"\t\t\t\t\t\t\t\t (_tước gỡ lỗi_)\n" +" --strip-unneeded \tGỡ bỏ mọi ký hiệu không cần thiết để định vị " +"lại\n" +"\t\t\t\t\t\t\t\t (_tước không cần thiết_)\n" +" -N --strip-symbol \t\t Đừng sao chép ký hiệu \n" +"\t\t\t\t\t\t\t\t (_tước ký hiệu_)\n" +" --strip-unneeded-symbol \n" +"\tĐừng sao chép ký hiệu trừ cần thiết để định vị lại (_tước không cần " +"thiết_)\n" +" --only-keep-debug\t\t\t\tTước hết, trừ thông tin gỡ lỗi\n" +"\t\t\t\t\t\t\t\t (_chỉ giữ gỡ lỗi_)\n" +" -K --keep-symbol \tChỉ sao chép ký hiệu \n" +"\t\t\t\t\t\t\t\t (_giữ ký hiệu_)\n" +" -L --localize-symbol \n" +"\t\t\t\tBuộc ký hiệu có nhãn điều cục bộ (_địa phương hóa_)\n" +" -G --keep-global-symbol \tĐịa phương hóa mọi ký hiệu trừ \n" +"\t\t\t\t\t\t\t\t (_giữ ký hiệu toàn cục_)\n" +" -W --weaken-symbol \tBuộc ký hiệu có nhãn điều " +"yếu\n" +"\t\t\t\t\t\t\t\t (_làm yếu ký hiệu_)\n" +" --weaken \t\tBuộc mọi ký hiệu toàn cục có nhãn " +"điều yếu\n" +"\t\t\t\t\t\t\t\t (_làm yếu đi_)\n" +" -w --wildcard \t\tCho phép _ký tự đại diện_ trong sự so sánh ký " +"hiệu\n" +" -x --discard-all \t\t\tGỡ bỏ mọi ký hiệu không toàn cục\n" +"\t\t\t\t\t\t\t\t (_hủy hết_)\n" +" -X --discard-locals Gỡ bỏ ký hiệu nào được tạo ra bởi bộ biên " +"dịch\n" +"\t\t\t\t\t\t\t\t (_hủy các điều cục bộ_)\n" +" -i --interleave \t\t\tChỉ sao chép một của mỗi byte\n" +"\t\t\t\t\t\t\t\t (_chen vào_)\n" +" -b --byte \n" +"\t\t\t\tChọn byte số thứ tự trong mỗi khối tin đã chen vào\n" +" --gap-fill \t_Điền vào khe_ giữa hai phần bằng " +"\n" +" --pad-to <địa_chỉ>\t\t_Đệm_ phần cuối cùng _đế_n địa chỉ <địa_chỉ>\n" +" --set-start <địa_chỉ> \t\t_Lập_ địa chỉ _đầu_ thành " +"<địa_chỉ>\n" +" {--change-start|--adjust-start} \n" +"\t\tThêm vào địa chỉ đầu (_thay đổi đầu, điều chỉnh đầu_)\n" +" {--change-addresses|--adjust-vma} \n" +" \t\t\t\t\t\t\tThêm vào địa chỉ đầu, LMA và VMA\n" +"\t\t\t\t\t\t\t (_thay đổi địa chỉ, điều chỉnh vma_)\n" +" {--change-section-address|--adjust-section-vma} {=|+|-}\n" +"\t\t\t\t\tThay đổi LMA và VMA của phần bằng \n" +"\t\t\t\t\t\t(_thay đổi địa chỉ phần, điều chỉnh vma phần_)\n" +" --change-section-lma {=|+|-}\n" +" \tThay đổi LMA của phần bằng (_thay đổi LMA của phần_)\n" +" --change-section-vma {=|+|-}\n" +" \tThay đổi VMA của phần bằng (_thay đổi VMA của phần_)\n" +" {--[no-]change-warnings|--[no-]adjust-warnings}\n" +" \t\t\t\t\t\t\t\tCảnh báo nếu không có phần có tên\n" +"\t\t(_[không] thay đổi các cảnh báo, [không] điều chỉnh các cảnh báo_)\n" +" --set-section-flags =\n" +" \t\tLập thuộc tính của phần thành " +"\n" +"\t\t\t\t\t\t\t\t (_lập các cờ phần_)\n" +" --add-section =\n" +"\t\t\t\t_Thêm phần_ được tìm trong vào kết xuất\n" +" --rename-section =[,]\n" +"\t\t\t\t\t\t\t\t_Thay đổi phần_ thành \n" +" --change-leading-char\n" +"\t\t\t\t\tBuộc kiểu dáng của ký tự đi trước của dạng thức xuất\n" +"\t\t\t\t\t\t\t\t (_thay đổi ký tự đi trước_)\n" +" --remove-leading-char\t\t_Gỡ bỏ ký tự đi trước_ ra các ký hiệu toàn " +"cục\n" +" --redefine-sym =\n" +"\t\t\t\t\t\t_Định nghĩa lại_ tên _ký hiệu_ thành \n" +" --redefine-syms \n" +"\t\t« --redefine-sym » cho mọi cặp ký hiệu được liệt kê trong \n" +" --srec-len \t\tGiới hạn _độ dài_ của các Srecords đã tạo " +"ra\n" +" --srec-forceS3 \tGiới hạn kiểu Srecords thành S3 " +"(_buộc_)\n" +" --strip-symbols \n" +"\t« -N » cho mọi ký hiệu được liệt kê trong (_tước các ký hiệu_)\n" +" --strip-unneeded-symbols \n" +" \t\t\t\t\t\t\t« --strip-unneeded-symbol » cho mọi ký hiệu\n" +"\t\t\t\t\t\t\t\tđược liệt kê trong \n" +" --keep-symbols \n" +"\t\t\t\t\t« -K » cho mọi ký hiệu được liệt kê trong \n" +"\t\t\t\t\t\t\t\t (_giữ các ký hiệu_)\n" +" --localize-symbols \n" +"\t\t\t\t\t« -L » cho mọi ký hiệu được liệt kê trong \n" +"\t\t\t\t\t\t\t\t (_địa phương hóa các ký hiệu_)\n" +" --keep-global-symbols \n" +"\t\t\t\t\t« -G » cho mọi ký hiệu được liệt kê trong \n" +"\t\t\t\t\t\t\t\t (_giữ các ký hiệu toàn cục_)\n" +" --weaken-symbols \n" +"\t\t\t\t\t« -W » cho mọi ký hiệu được liệt kê trong \n" +"\t\t\t\t\t\t\t\t (_làm yếu các ký hiệu_)\n" +" --alt-machine-code Dùng _mã máy xen kẽ_ cho kết xuất\n" +" --writable-text \t\tĐánh dấu _văn bản_ xuất _có khả năng " +"ghi_\n" +" --readonly-text \tLàm cho vân bản xuất được bảo vậ chống " +"ghi\n" +"\t\t\t\t\t\t\t\t (_văn bản chỉ có khả năng đọc_)\n" +" --pure\n" +"\t\t\tĐánh dấu tập tin xuất sẽ có trang được sắp xếp theo yêu cầu\n" +"\t\t\t\t\t\t\t\t (_tinh khiết_)\n" +" --impure \t\tĐánh dấu tập tin xuất _không tinh " +"khiết_\n" +" --prefix-symbols \n" +"\t\tThêm vào đầu của mọi tên ký hiệu (_tiền tố các ký hiệu_)\n" +" --prefix-sections \n" +"\t\tThêm vào đầu của mọi tên phần (_tiền tố các phần_)\n" +" --prefix-alloc-sections \n" +"\t\tThêm vào đầu của mọi tên phần có thể cấp phát\n" +"\t\t\t\t\t\t\t\t(_tiền tố các phần có thể cấp phát_)\n" +" -v --verbose \t\tLiệt kê mọi tập tin đối tượng đã được " +"sửa đổi\n" +"\t\t\t\t\t\t\t\t (_chi tiết_)\n" +" -V --version Hiển thị số thứ tự _phiên bản_ của chương " +"trình này\n" +" -h --help \t\t\tHiển thị _trợ giúp_ này\n" +" --info \t\tLiệt kê các dạng thức và kiến trúc " +"được hỗ trợ\n" +"\t\t\t\t\t\t\t\t (_thông tin_)\n" + +#: objcopy.c:485 +#, c-format +msgid "Usage: %s in-file(s)\n" +msgstr "Cách sử dụng: %s tập_tin_nhập...\n" + +#: objcopy.c:486 +#, c-format +msgid " Removes symbols and sections from files\n" +msgstr " Gỡ bỏ ký hiệu và phần ra tập tin\n" + +#: objcopy.c:488 +#, c-format +msgid "" +" -I --input-target= Assume input file is in format \n" +" -O --output-target= Create an output file in format " +"\n" +" -F --target= Set both input and output format to " +"\n" +" -p --preserve-dates Copy modified/access timestamps to the " +"output\n" +" -R --remove-section= Remove section from the output\n" +" -s --strip-all Remove all symbol and relocation " +"information\n" +" -g -S -d --strip-debug Remove all debugging symbols & sections\n" +" --strip-unneeded Remove all symbols not needed by " +"relocations\n" +" --only-keep-debug Strip everything but the debug " +"information\n" +" -N --strip-symbol= Do not copy symbol \n" +" -K --keep-symbol= Only copy symbol \n" +" -w --wildcard Permit wildcard in symbol comparison\n" +" -x --discard-all Remove all non-global symbols\n" +" -X --discard-locals Remove any compiler-generated symbols\n" +" -v --verbose List all object files modified\n" +" -V --version Display this program's version number\n" +" -h --help Display this output\n" +" --info List object formats & architectures " +"supported\n" +" -o Place stripped output into \n" +msgstr "" +" -I --input-target= Giả sử tập tin nhập có dạng thức " +"\n" +"\t\t(đích nhập)\n" +" -O --output-target= Tạo một tập tin xuất có dạng thức \n" +"\t\t(đích xuất)\n" +" -F --target= Đặt dạng thức cả nhập lẫn xuất đều thành " +"\n" +"\t\t(đích)\n" +" -p --preserve-dates\n" +"\t\tSao chép các nhãn thời gian truy cập/đã sửa đổi vào kết xuất\n" +"\t\t(bảo tồn các ngày)\n" +" -R --remove-section= \t_Gỡ bỏ phần_ ra dữ liệu xuất\n" +" -s --strip-all \t\tGỡ bỏ mọi thông tin kiểu ký hiệu và " +"định vị lại\n" +"\t\t(tước hết)\n" +" -g -S -d --strip-debug \tGỡ bỏ mọi ký hiệu và phần kiểu gỡ lỗi\n" +"\t\t(tước gỡ lỗi)\n" +" --strip-unneeded Gỡ bỏ mọi ký hiệu không cần thiết khi " +"định vị lại\n" +"\t\t(tước không cần thiết)\n" +" --only-keep-debug \tTước hết, trừ thông tin gỡ lỗi\n" +"\t\t(chỉ giữ gỡ lỗi)\n" +" -N --strip-symbol= \tĐừng sao chép ký hiệu \n" +"\t\t(tước ký hiệu)\n" +" -K --keep-symbol= \tSao chép chỉ ký hiệu \n" +"\t\t(giữ ký hiệu)\n" +" -w --wildcard Cho phép _ký tự đại diện_ trong chuỗi so sánh " +"ký hiệu\n" +" -x --discard-all \t\tGỡ bỏ mọi ký hiệu không toàn cục\n" +"\t\t(hủy hết)\n" +" -X --discard-locals \tGỡ bo ký hiệu nào do bộ biên dịch tạo " +"ra\n" +"\t\t(hủy các điều cục bộ)\n" +" -v --verbose \t\tLiệt kê mọi tập tin đối tượng đã sửa " +"đổi\n" +"\t\t(chi tiết)\n" +" -V --version Hiển thị số thứ tự _phiên bản_ của chương " +"trình này\n" +" -h --help \t\tHiển thị _trợ giúp_ này\n" +" --info Liệt kê các dạng thức đối tượng và kiến trúc " +"được hỗ trợ\n" +"\t\t(thông tin) -o \tĐể kết _xuất_ đã " +"tướng vào \n" + +#: objcopy.c:560 +#, c-format +msgid "unrecognized section flag `%s'" +msgstr "không nhận ra cờ phần « %s »" + +#: objcopy.c:561 +#, c-format +msgid "supported flags: %s" +msgstr "các cờ đã hỗ trợ : %s" + +#: objcopy.c:638 +#, c-format +msgid "cannot open '%s': %s" +msgstr "không thể mở « %s »: %s" + +#: objcopy.c:641 objcopy.c:2629 +#, c-format +msgid "%s: fread failed" +msgstr "%s: việc fread (đọc f) bị lỗi" + +#: objcopy.c:714 +#, c-format +msgid "%s:%d: Ignoring rubbish found on this line" +msgstr "%s:%d: Đang bỏ qua rác được gặp trên dòng này" + +#: objcopy.c:976 +#, c-format +msgid "%s: Multiple redefinition of symbol \"%s\"" +msgstr "%s: Ký hiệu « %s » đã được định nghĩa lại nhiều lần" + +#: objcopy.c:980 +#, c-format +msgid "%s: Symbol \"%s\" is target of more than one redefinition" +msgstr "%s: Ký hiệu « %s » là đích của nhiều lời định nghĩa lại" + +#: objcopy.c:1008 +#, c-format +msgid "couldn't open symbol redefinition file %s (error: %s)" +msgstr "không thể mở tập tin định nghĩa lại ký hiệu %s (lỗi: %s)" + +#: objcopy.c:1086 +#, c-format +msgid "%s:%d: garbage found at end of line" +msgstr "%s:%d: gặp rác tại kết thúc dòng" + +#: objcopy.c:1089 +#, c-format +msgid "%s:%d: missing new symbol name" +msgstr "%s:%d: thiếu tên ký hiệu mới" + +#: objcopy.c:1099 +#, c-format +msgid "%s:%d: premature end of file" +msgstr "%s:%d: gặp kết thúc tập tin quá sớm" + +#: objcopy.c:1124 +msgid "Unable to change endianness of input file(s)" +msgstr "Không thể thay đổi tính trạng cuối (endian) của (các) tập tin nhập" + +#: objcopy.c:1133 +#, c-format +msgid "copy from %s(%s) to %s(%s)\n" +msgstr "chép từ %s(%s) đến %s(%s)\n" + +#: objcopy.c:1170 +#, c-format +msgid "Unable to recognise the format of the input file %s" +msgstr "Không thể nhận diện dạng thức của tập tin nhập %s" + +#: objcopy.c:1174 +#, c-format +msgid "Warning: Output file cannot represent architecture %s" +msgstr "Cảnh báo : Tập tin xuất không thể tiêu biểu kiến trúc %s" + +#: objcopy.c:1211 +#, c-format +msgid "can't create section `%s': %s" +msgstr "không thể tạo phần « %s »: %s" + +#: objcopy.c:1277 +msgid "there are no sections to be copied!" +msgstr "• Không có phần cần sao chép. •" + +#: objcopy.c:1323 +#, c-format +msgid "Can't fill gap after %s: %s" +msgstr "Không thể điền vào khe sau : %s: %s" + +#: objcopy.c:1348 +#, c-format +msgid "Can't add padding to %s: %s" +msgstr "Không thể thêm đệm vào %s: %s" + +#: objcopy.c:1514 +#, c-format +msgid "%s: error copying private BFD data: %s" +msgstr "%s: gặp lỗi khi sao chép dữ liệu BFD riêng : %s" + +#: objcopy.c:1525 +msgid "unknown alternate machine code, ignored" +msgstr "không biết mã máy xen kẽ nên bỏ qua nó" + +#: objcopy.c:1555 objcopy.c:1585 +#, c-format +msgid "cannot mkdir %s for archive copying (error: %s)" +msgstr "không thể mkdir (tạo thư mục) %s để sao chép kho (lỗi: %s)" + +#: objcopy.c:1790 +#, c-format +msgid "Multiple renames of section %s" +msgstr "Đã thay đổi tên phần %s nhiều lần" + +#: objcopy.c:1841 +msgid "private header data" +msgstr "dữ liệu dòng đầu riêng" + +#: objcopy.c:1849 +#, c-format +msgid "%s: error in %s: %s" +msgstr "%s: lỗi trong %s: %s" + +#: objcopy.c:1903 +msgid "making" +msgstr "làm" + +#: objcopy.c:1912 src/main/extractor.c:87 +msgid "size" +msgstr "cỡ" + +#: objcopy.c:1926 +msgid "vma" +msgstr "vma" + +#: objcopy.c:1951 lexsup.c:1101 +msgid "alignment" +msgstr "canh lề" + +#: objcopy.c:1966 lib/report.c:103 lib/report.c:415 +msgid "flags" +msgstr "cờ" + +#: objcopy.c:1988 +msgid "private data" +msgstr "dữ liệu riêng" + +#: objcopy.c:1996 +#, c-format +msgid "%s: section `%s': error in %s: %s" +msgstr "%s: phần « %s »: lỗi trong %s: %s" + +#: objcopy.c:2274 +#, c-format +msgid "%s: can't create debugging section: %s" +msgstr "%s: không thể tạo phần gỡ lỗi: %s" + +#: objcopy.c:2288 +#, c-format +msgid "%s: can't set debugging section contents: %s" +msgstr "%s: không thể đặt nội dung phần gỡ lỗi: %s" + +#: objcopy.c:2297 +#, c-format +msgid "%s: don't know how to write debugging information for %s" +msgstr "%s: không biết cách ghi thông tin gỡ lỗi cho %s" + +#: objcopy.c:2472 +msgid "byte number must be non-negative" +msgstr "số byte phải là không âm" + +#: objcopy.c:2482 +msgid "interleave must be positive" +msgstr "chen vào phải là dương" + +#: objcopy.c:2502 objcopy.c:2510 +#, c-format +msgid "%s both copied and removed" +msgstr "%s cả được sao chép lẫn bị gỡ bỏ đều" + +#: objcopy.c:2603 objcopy.c:2674 objcopy.c:2774 objcopy.c:2805 objcopy.c:2829 +#: objcopy.c:2833 objcopy.c:2853 +#, c-format +msgid "bad format for %s" +msgstr "dạng thức sai cho %s" + +#: objcopy.c:2624 +#, c-format +msgid "cannot open: %s: %s" +msgstr "không thể mở : %s: %s" + +#: objcopy.c:2743 +#, c-format +msgid "Warning: truncating gap-fill from 0x%s to 0x%x" +msgstr "Cảnh báo : đang cắt xén điền-khe từ 0x%s thành 0x%x" + +#: objcopy.c:2903 +msgid "alternate machine code index must be positive" +msgstr "chỉ mục mã máy xen kẽ phải là dương" + +#: objcopy.c:2961 +msgid "byte number must be less than interleave" +msgstr "số byte phải là ít hơn chen vào" + +#: objcopy.c:2991 +#, c-format +msgid "architecture %s unknown" +msgstr "không biết kiến trúc %s" + +#: objcopy.c:2995 +msgid "" +"Warning: input target 'binary' required for binary architecture parameter." +msgstr "" +"Cảnh báo : đích nhập « binary » (nhị phân) cần thiết cho tham số kiến trúc " +"nhị phân." + +#: objcopy.c:2996 +#, c-format +msgid " Argument %s ignored" +msgstr " Đối số %s bị bỏ qua" + +#: objcopy.c:3002 +#, c-format +msgid "warning: could not locate '%s'. System error message: %s" +msgstr "cảnh báo : không thể định vị « %s ». Thông điệp lỗi hệ thống: %s" + +#: objcopy.c:3042 objcopy.c:3056 +#, c-format +msgid "%s %s%c0x%s never used" +msgstr "%s %s%c0x%s chưa bao giờ dùng" + +#: objdump.c:176 +#, c-format +msgid "Usage: %s \n" +msgstr "Cách sử dụng: %s \n" + +#: objdump.c:177 +#, c-format +msgid " Display information from object .\n" +msgstr " Hiển thị thông tin từ đối tượng.\n" + +#: objdump.c:178 +#, c-format +msgid " At least one of the following switches must be given:\n" +msgstr " Phải đưa ra ít nhất một của những cái chuyển theo sau :\n" + +#: objdump.c:179 +#, c-format +msgid "" +" -a, --archive-headers Display archive header information\n" +" -f, --file-headers Display the contents of the overall file header\n" +" -p, --private-headers Display object format specific file header " +"contents\n" +" -h, --[section-]headers Display the contents of the section headers\n" +" -x, --all-headers Display the contents of all headers\n" +" -d, --disassemble Display assembler contents of executable " +"sections\n" +" -D, --disassemble-all Display assembler contents of all sections\n" +" -S, --source Intermix source code with disassembly\n" +" -s, --full-contents Display the full contents of all sections " +"requested\n" +" -g, --debugging Display debug information in object file\n" +" -e, --debugging-tags Display debug information using ctags style\n" +" -G, --stabs Display (in raw form) any STABS info in the file\n" +" -t, --syms Display the contents of the symbol table(s)\n" +" -T, --dynamic-syms Display the contents of the dynamic symbol table\n" +" -r, --reloc Display the relocation entries in the file\n" +" -R, --dynamic-reloc Display the dynamic relocation entries in the " +"file\n" +" -v, --version Display this program's version number\n" +" -i, --info List object formats and architectures supported\n" +" -H, --help Display this information\n" +msgstr "" +" -a, --archive-headers \t\tHiển thị thông tin về _các phần đầu kho_\n" +" -f, --file-headers Hiển thị nội dung của _toàn bộ phần đầu tập " +"tin_\n" +" -p, --private-headers\n" +"\t\tHiển thị nội dung của phần đầu tập tin đặc trưng cho đối tượng\n" +"\t\t(các phần đầu riêng)\n" +" -h, --[section-]headers Hiển thị nội dung của _các phần đầu của phần_\n" +" -x, --all-headers \t\t Hiển thị nội dung của _mọi phần đầu_\n" +" -d, --disassemble\n" +"\t\tHiển thị nội dung của các phần có khả năng thực hiện\n" +"\t\t(rã)\n" +" -D, --disassemble-all \t Hiển thị nội dung dịch mã số của mọi phần\n" +"\t\t(rã hết)\n" +" -S, --source \t\t\t\t Trộn lẫn mã _nguồn_ với việc rã\n" +" -s, --full-contents Hiển thị _nội dung đầy đủ_ của mọi phần đã yêu cầu\n" +" -g, --debugging Hiển thị thông tin _gỡ lỗi_ trong tập tin đối " +"tượng\n" +" -e, --debugging-tags Hiển thị thông tin gỡ lỗi, dùng kiểu dáng ctags\n" +"\t\t(các thẻ gỡ lỗi)\n" +" -G, --stabs Hiển thị (dạng thô) thông tin STABS nào trong thông " +"tin\n" +" -t, --syms \t\t\t Hiển thị nội dung của các bảng ký hiệu\n" +"\t\t(các ký hiệu [viết tắt])\n" +" -T, --dynamic-syms \t\tHiển thị nội dung của bảng ký hiệu động\n" +"\t\t(các ký hiệu động [viết tắt])\n" +" -r, --reloc \t\tHiển thị các mục nhập định vị lại trong tập " +"tin\n" +"\t\t(định vị lại [viết tắt])\n" +" -R, --dynamic-reloc\n" +"\t\t\t\t Hiển thị các mục nhập định vị lại động trong tập tin\n" +"\t\t(định vị lại động [viết tắt])\n" +" -v, --version Hiển thị số thự tự _phiên bản_ của chương trình " +"này\n" +" -i, --info Liệt kê các dạng thức đối tượng và kiến trúc được hỗ " +"trợ\n" +"\t\t(thông tin [viết tắt])\n" +" -H, --help \tHiển thị _trợ giúp_ này\n" + +#: objdump.c:202 +#, c-format +msgid "" +"\n" +" The following switches are optional:\n" +msgstr "" +"\n" +" Những cái chuyển theo đây là tùy chọn:\n" + +#: objdump.c:203 +#, c-format +msgid "" +" -b, --target=BFDNAME Specify the target object format as " +"BFDNAME\n" +" -m, --architecture=MACHINE Specify the target architecture as MACHINE\n" +" -j, --section=NAME Only display information for section NAME\n" +" -M, --disassembler-options=OPT Pass text OPT on to the disassembler\n" +" -EB --endian=big Assume big endian format when " +"disassembling\n" +" -EL --endian=little Assume little endian format when " +"disassembling\n" +" --file-start-context Include context from start of file (with -" +"S)\n" +" -I, --include=DIR Add DIR to search list for source files\n" +" -l, --line-numbers Include line numbers and filenames in " +"output\n" +" -C, --demangle[=STYLE] Decode mangled/processed symbol names\n" +" The STYLE, if specified, can be `auto', " +"`gnu',\n" +" `lucid', `arm', `hp', `edg', `gnu-v3', " +"`java'\n" +" or `gnat'\n" +" -w, --wide Format output for more than 80 columns\n" +" -z, --disassemble-zeroes Do not skip blocks of zeroes when " +"disassembling\n" +" --start-address=ADDR Only process data whose address is >= ADDR\n" +" --stop-address=ADDR Only process data whose address is <= ADDR\n" +" --prefix-addresses Print complete address alongside " +"disassembly\n" +" --[no-]show-raw-insn Display hex alongside symbolic disassembly\n" +" --adjust-vma=OFFSET Add OFFSET to all displayed section " +"addresses\n" +" --special-syms Include special symbols in symbol dumps\n" +"\n" +msgstr "" +" -b, --target=TÊN_BFD \tGhi rõ dạng thức đối tượng _đích_ là TÊN_BFD\n" +" -m, --architecture=MÁY \t\t Ghi rõ _kiến trúc_ đích là MÁY\n" +" -j, --section=TÊN \t\t Hiển thị thông tin chỉ cho _phần_ " +"TÊN\n" +" -M, --disassembler-options=TÙY_CHỌN\n" +"\t\tGởi chuỗi TÙY_CHỌN qua cho _bộ rã_\n" +"\t\t(các tùy chọn bộ rã)\n" +" -EB --endian=big\n" +"\t\tGiả sử dạng thức tính trạng cuối lớn (big-endian) khi rã\n" +" -EL --endian=little\n" +"\t\tGiả sử dạng thức tính trạng cuối nhỏ (little-endian) khi rã\n" +" --file-start-context \tGồm _ngữ cảnh_ từ _đầu tập tin_ (bằng « -" +"S »)\n" +" -I, --include=THƯ_MỤC\n" +"\t\tThêm THƯ_MỤC vào danh sách tìm kiếm tập tin nguồn\n" +"\t\t(bao gồm)\n" +" -l, --line-numbers\n" +"\t\tGồm các _số thứ tự dòng_ và tên tập tin trong kết xuất\n" +" -C, --demangle[=KIỂU_DÁNG] giải mã các tên ký hiệu đã rối/xử lý\n" +"\t\t(tháo gỡ)\n" +"\t\tKIỂU_DÁNG, nếu đã ghi rõ, có thể là:\n" +"\t\t • auto\t\ttự động\n" +"\t\t • gnu\n" +" \t • lucid\t\trõ ràng\n" +"\t\t • arm\n" +"\t\t • hp\n" +"\t\t • edg\n" +"\t\t • gnu-v3\n" +" \t\t • java\n" +" \t • gnat\n" +" -w, --wide \t\tĐịnh dạng dữ liệu xuất chiếm hơn 80 " +"cột\n" +"\t\t(rộng)\n" +" -z, --disassemble-zeroes \t\tĐừng nhảy qua khối ố không khi rã\n" +"\t\t(rã các số không)\n" +" --start-address=ĐỊA_CHỈ Xử lý chỉ dữ liệu có địa chỉ ≥ " +"ĐỊA_CHỈ\n" +" --stop-address=ĐỊA_CHỈ Xử lý chỉ dữ liệu có địa chỉ ≤ " +"ĐỊA_CHỈ\n" +" --prefix-addresses \t\tIn ra địa chỉ hoàn toàn ở b việc " +"rã\n" +"\t\t(thêm vào đầu các địa chỉ)\n" +" --[no-]show-raw-insn\n" +"\t\tHiển thị thập lục phân ở bên việc rã kiểu ký hiệu\n" +"hông] hiển thị câu lệnh thô)\n" +" --adjust-vma=HIỆU_SỐ\n" +"\t\tThêm HIỆU_SỐ vào mọi địa chỉ phần đã hiển thị\n" +"\t\t(điều chỉnh vma) --special-syms Gồm _các ký hiệu đặc biệt_ " +"trong việc đổ ký hiệu\n" +"\n" + +#: objdump.c:378 +#, c-format +msgid "Sections:\n" +msgstr "Phần:\n" + +#: objdump.c:381 objdump.c:385 +#, c-format +msgid "Idx Name Size VMA LMA File off Algn" +msgstr "Idx Name Size VMA LMA File off Algn" + +#: objdump.c:387 +#, c-format +msgid "" +"Idx Name Size VMA LMA File off " +"Algn" +msgstr "" +"Idx Name Size VMA LMA File off " +"Algn" + +#: objdump.c:391 +#, c-format +msgid " Flags" +msgstr " Cờ" + +#: objdump.c:393 +#, c-format +msgid " Pg" +msgstr " Tr" + +#: objdump.c:436 +#, c-format +msgid "%s: not a dynamic object" +msgstr "%s không phải là môt đối tượng động" + +#: objdump.c:1722 +#, c-format +msgid "Disassembly of section %s:\n" +msgstr "Việc rã phần %s:\n" + +#: objdump.c:1884 +#, c-format +msgid "Can't use supplied machine %s" +msgstr "Không thể sử dụng máy đã cung cấp %s" + +#: objdump.c:1903 +#, c-format +msgid "Can't disassemble for architecture %s\n" +msgstr "Không thể rã cho kiến trúc %s\n" + +#: objdump.c:1973 +#, c-format +msgid "" +"No %s section present\n" +"\n" +msgstr "" +"Không có phần %s ở\n" +"\n" + +#: objdump.c:1982 +#, c-format +msgid "Reading %s section of %s failed: %s" +msgstr "Việc đọc phần %s của %s bị lỗi: %s" + +#: objdump.c:2026 +#, c-format +msgid "" +"Contents of %s section:\n" +"\n" +msgstr "" +"Nội dung phần %s\n" +"\n" + +#: objdump.c:2153 +#, c-format +msgid "architecture: %s, " +msgstr "kiến trúc: %s, " + +#: objdump.c:2156 +#, c-format +msgid "flags 0x%08x:\n" +msgstr "cờ 0x%08x:\n" + +#: objdump.c:2170 +#, c-format +msgid "" +"\n" +"start address 0x" +msgstr "" +"\n" +"địa chỉ đầu 0x" + +#: objdump.c:2210 +#, c-format +msgid "Contents of section %s:\n" +msgstr "Nội dung phần %s:\n" + +#: objdump.c:2335 +#, c-format +msgid "no symbols\n" +msgstr "không có ký hiệu\n" + +#: objdump.c:2342 +#, c-format +msgid "no information for symbol number %ld\n" +msgstr "không có thông tin cho ký hiệu số %ld\n" + +#: objdump.c:2345 +#, c-format +msgid "could not determine the type of symbol number %ld\n" +msgstr "không thể quyết định kiểu ký hiệu số %ld\n" + +#: objdump.c:2611 +#, c-format +msgid "" +"\n" +"%s: file format %s\n" +msgstr "" +"\n" +"%s: dạng thức tập tin %s\n" + +#: objdump.c:2662 +#, c-format +msgid "%s: printing debugging information failed" +msgstr "%s: việc in ra thông tin gỡ lỗi bị lỗi" + +#: objdump.c:2753 +#, c-format +msgid "In archive %s:\n" +msgstr "Trong kho %s\n" + +#: objdump.c:2873 +msgid "unrecognized -E option" +msgstr "không nhận ra tùy chọn « -E »" + +#: objdump.c:2884 +#, c-format +msgid "unrecognized --endian type `%s'" +msgstr "không nhận ra kiểu tính trạng cuối (endian) « %s »" + +#: rdcoff.c:196 +#, c-format +msgid "parse_coff_type: Bad type code 0x%x" +msgstr "parse_coff_type: (phân tách kiểu coff) Mã kiểu sai 0x%x" + +#: rdcoff.c:404 rdcoff.c:509 rdcoff.c:697 +#, c-format +msgid "bfd_coff_get_syment failed: %s" +msgstr "« bfd_coff_get_syment » bị lỗi: %s" + +#: rdcoff.c:420 rdcoff.c:717 +#, c-format +msgid "bfd_coff_get_auxent failed: %s" +msgstr "« bfd_coff_get_auxent » bị lỗi: %s" + +#: rdcoff.c:784 +#, c-format +msgid "%ld: .bf without preceding function" +msgstr "%ld: « .bf » không có hàm đi trước" + +#: rdcoff.c:834 +#, c-format +msgid "%ld: unexpected .ef\n" +msgstr "%ld: « .ef » bất ngờ\n" + +#: rddbg.c:85 +#, c-format +msgid "%s: no recognized debugging information" +msgstr "%s: không có thông tin gỡ lỗi đã nhận ra" + +#: rddbg.c:394 +#, c-format +msgid "Last stabs entries before error:\n" +msgstr "Những mục nhập stabs cuối cùng trước lỗi:\n" + +#: readelf.c:272 ia64-gen.c:297 +#, c-format +msgid "%s: Error: " +msgstr "%s: Lỗi: " + +#: readelf.c:283 ia64-gen.c:310 +#, c-format +msgid "%s: Warning: " +msgstr "%s: Cảnh báo : " + +#: readelf.c:298 +#, c-format +msgid "Unable to seek to 0x%x for %s\n" +msgstr "Không thể nhảy tới 0x%x tìm %s\n" + +#: readelf.c:310 +#, c-format +msgid "Out of memory allocating 0x%x bytes for %s\n" +msgstr "Hết bộ nhớ khi cấp phát 0x%x byte cho %s\n" + +#: readelf.c:318 +#, c-format +msgid "Unable to read in 0x%x bytes of %s\n" +msgstr "Không thể đọc trong 0x%x byte của %s\n" + +#: readelf.c:364 readelf.c:412 readelf.c:615 readelf.c:647 +#, c-format +msgid "Unhandled data length: %d\n" +msgstr "Độ dài dữ liệu không được quản lý: %d\n" + +#: readelf.c:752 +msgid "Don't know about relocations on this machine architecture\n" +msgstr "Không biết về việc định vị lại trên kiến trúc máy này\n" + +#: readelf.c:772 readelf.c:799 readelf.c:842 readelf.c:869 +msgid "relocs" +msgstr "đ.v. lại" + +#: readelf.c:782 readelf.c:809 readelf.c:852 readelf.c:879 +msgid "out of memory parsing relocs" +msgstr "hết bộ nhớ khi phân tách việc định vị lại" + +#: readelf.c:933 +#, c-format +msgid "" +" Offset Info Type Sym. Value Symbol's Name + Addend\n" +msgstr "" +" Hiệu Tin Kiểu Giá trị ký hiệu Tên ký hiệu + gì thêm\n" + +#: readelf.c:935 +#, c-format +msgid " Offset Info Type Sym.Value Sym. Name + Addend\n" +msgstr " HIệu Tin Kiểu Giá trị ký hiệu Tên ký hiệu + gì thêm\n" + +#: readelf.c:940 +#, c-format +msgid " Offset Info Type Sym. Value Symbol's Name\n" +msgstr " HIệu Tin Kiểu Giá trị ký hiệu Tên ký hiệu\n" + +#: readelf.c:942 +#, c-format +msgid " Offset Info Type Sym.Value Sym. Name\n" +msgstr " Hiệu Tin Kiểu Giá trị ký hiệu Tên ký hiệu\n" + +#: readelf.c:950 +#, c-format +msgid "" +" Offset Info Type Symbol's Value " +"Symbol's Name + Addend\n" +msgstr "" +" Offset Info Type Symbol's Value " +"Symbol's Name + Addend\n" + +#: readelf.c:952 +#, c-format +msgid "" +" Offset Info Type Sym. Value Sym. Name + " +"Addend\n" +msgstr "" +" Hiệu Tin Kiểu Giá trị ký hiệu Tên ký hiệu + gì thêm\n" + +#: readelf.c:957 +#, c-format +msgid "" +" Offset Info Type Symbol's Value " +"Symbol's Name\n" +msgstr "" +" Offset Info Type Symbol's Value " +"Symbol's Name\n" + +#: readelf.c:959 +#, c-format +msgid "" +" Offset Info Type Sym. Value Sym. Name\n" +msgstr " HIệu Tin Kiểu Giá trị ký hiệu Tên ký hiệu\n" + +#: readelf.c:1239 readelf.c:1241 readelf.c:1324 readelf.c:1326 readelf.c:1335 +#: readelf.c:1337 +#, c-format +msgid "unrecognized: %-7lx" +msgstr "không nhận ra: %-7lx" + +#: readelf.c:1295 +#, c-format +msgid "" +msgstr "" + +#: readelf.c:1297 +#, c-format +msgid "" +msgstr "" + +#: readelf.c:1569 +#, c-format +msgid "Processor Specific: %lx" +msgstr "Đặc trưng cho bộ xử lý: %lx" + +#: readelf.c:1588 +#, c-format +msgid "Operating System specific: %lx" +msgstr "Đặc trưng cho Hệ điều hành: %lx" + +#: readelf.c:1592 readelf.c:2370 +#, c-format +msgid ": %lx" +msgstr ": %lx" + +#: readelf.c:1605 +msgid "NONE (None)" +msgstr "KHÔNG CÓ (Không có)" + +#: readelf.c:1606 +msgid "REL (Relocatable file)" +msgstr "REL (Tập tin có thể _định vị lại_)" + +#: readelf.c:1607 +msgid "EXEC (Executable file)" +msgstr "EXEC (Executable file)" + +#: readelf.c:1608 +msgid "DYN (Shared object file)" +msgstr "DYN (Shared object file)" + +#: readelf.c:1609 +msgid "CORE (Core file)" +msgstr "CORE (Core file)" + +#: readelf.c:1613 +#, c-format +msgid "Processor Specific: (%x)" +msgstr "Đặc trưng cho bộ xử lý: (%x)" + +#: readelf.c:1615 +#, c-format +msgid "OS Specific: (%x)" +msgstr "Đặc trưng cho HĐH: (%x)" + +#: readelf.c:1617 readelf.c:1724 readelf.c:2554 +#, c-format +msgid ": %x" +msgstr ": %x" + +#. #-#-#-#-# guikachu.vi.po (guikachu HEAD) #-#-#-#-# +#. Fill the model +#: ../src/mlview-node-editor.cc:1992 ../gnome/applet/wso-none.c:53 +#: ../storage/sunone-invitation-list.c:291 ../widgets/gtk+.xml.in.h:126 +#: ../src/form-editor/button-prop.cc:144 datebook_gui.c:1338 +#: datebook_gui.c:4626 libexif/olympus/mnote-olympus-entry.c:290 +#: app/sample-editor.c:299 app/track-editor.c:190 app/track-editor.c:200 +msgid "None" +msgstr "Không có" + +#: readelf.c:2229 +msgid "Standalone App" +msgstr "Ứng dụng Độc lập" + +#: readelf.c:2232 readelf.c:2952 readelf.c:2968 +#, c-format +msgid "" +msgstr "" + +#: readelf.c:2597 +#, c-format +msgid "Usage: readelf elf-file(s)\n" +msgstr "Cách sử dụng: readelf tập_tin_elf...\n" + +#: readelf.c:2598 +#, c-format +msgid " Display information about the contents of ELF format files\n" +msgstr " Hiển thị thông tin về nội dung tập tin dạng thức ELF\n" + +#: readelf.c:2599 +#, c-format +msgid "" +" Options are:\n" +" -a --all Equivalent to: -h -l -S -s -r -d -V -A -I\n" +" -h --file-header Display the ELF file header\n" +" -l --program-headers Display the program headers\n" +" --segments An alias for --program-headers\n" +" -S --section-headers Display the sections' header\n" +" --sections An alias for --section-headers\n" +" -g --section-groups Display the section groups\n" +" -e --headers Equivalent to: -h -l -S\n" +" -s --syms Display the symbol table\n" +" --symbols An alias for --syms\n" +" -n --notes Display the core notes (if present)\n" +" -r --relocs Display the relocations (if present)\n" +" -u --unwind Display the unwind info (if present)\n" +" -d --dynamic Display the dynamic section (if present)\n" +" -V --version-info Display the version sections (if present)\n" +" -A --arch-specific Display architecture specific information (if " +"any).\n" +" -D --use-dynamic Use the dynamic section info when displaying " +"symbols\n" +" -x --hex-dump= Dump the contents of section \n" +" -w[liaprmfFsoR] or\n" +" --debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str," +"=loc,=Ranges]\n" +" Display the contents of DWARF2 debug sections\n" +msgstr "" +" Tùy chọn:\n" +" -a --all \t\t\t\t\tBằng: -h -l -S -s -r -d -V -A -I\n" +"\t(hết)\n" +" -h --file-header \t\t\t\tHiển thị _dòng đầu tập tin_ ELF\n" +" -l --program-headers \t\tHiển thị _các dòng đầu chương trình_\n" +" --segments \t\t\tBiệt hiệu cho « --program-headers »\n" +"\t(các phân đoạn)\n" +" -S --section-headers \t\t\tHiển thị dòng đầu của các phần\n" +"\t(các dòng đầu phần)\n" +" --sections \t\t\tBiệt hiệu cho « --section-headers »\n" +"\t(các phần)\n" +" -g --section-groups \t\t\t Hiển thị _các nhóm phần_\n" +" -e --headers \t\t\t\tBằng: -h -l -S\n" +"\t(các dòng đầu)\n" +" -s --syms \t\t\tHiển thị bảng _ký hiệu_\n" +" --symbols \t\t\tBiệt hiệu cho « --syms »\n" +"\t(các ký hiệu [« syms » là viết tắt])\n" +" -n --notes \t\t\tHiển thị _các ghi chú_ lõi (nếu có)\n" +" -r --relocs \t\tHiển thị _các việc định vị lại_ (nếu có)\n" +" -u --unwind \t\tHiển thị thông tin _tri ra_ (nếu có)\n" +" -d --dynamic \t\tHiển thị phần _động_ (nếu có)\n" +" -V --version-info \t\tHiển thị các phần phiên bản (nếu có)\n" +"\t(thông tin phiên bản)\n" +" -A --arch-specific Hiển thị thông tin _đặc trưng cho kiến trúc_ (nếu " +"có)\n" +" -D --use-dynamic _Dùng_ thông tin phần _động_ khi hiển thị ký hiệu\n" +" -x --hex-dump= \t\t\tĐổ nội dung phần \n" +"\t(đổ thập lục)\n" +" -w[liaprmfFsoR] or\n" +" --debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str," +"=loc,=Ranges]\n" +"\t[line\t\t\tdòng\n" +"\tinfo\t\t\tthông tin\n" +"\tabbrev.\t\tviết tắt\n" +"\tpubnames\tcác tên công\n" +"\taranges\t\tcác phạm vị a\n" +"\tmacro\t\tbộ lệnh\n" +"\tframes\t\tcác khung\n" +"\tstr\t\t\tchuỗi\n" +"\tloc\t\t\tđịnh vị\n" +"\tRanges\t\tCác phạm vị]\n" +" Hiển thị nội dung các phần gỡ lỗi kiểu DWARF2\n" + +#: readelf.c:2622 +#, c-format +msgid "" +" -i --instruction-dump=\n" +" Disassemble the contents of section \n" +msgstr "" +" -i --instruction-dump=\t\tTháo ra nội dung phần \n" +"\t(đổ câu lệnh)\n" + +#: readelf.c:2626 +#, c-format +msgid "" +" -I --histogram Display histogram of bucket list lengths\n" +" -W --wide Allow output width to exceed 80 characters\n" +" -H --help Display this information\n" +" -v --version Display the version number of readelf\n" +msgstr "" +" -I --histogram\n" +"\tHiển thị _biểu đồ tần xuất_ của các độ dài danh sách xô\n" +" -W --wide Cho phép độ _rộng_ kết xuất vượt qua 80 ký tự\n" +" -H --help \tHiển thị _trợ giúp_ này\n" +" -v --version \tHiển thị số thứ tự _phiên bản_ của readelf\n" + +#: readelf.c:2651 readelf.c:12118 +msgid "Out of memory allocating dump request table." +msgstr "Hết bộ nhớ khi cấp phát bảng yêu cầu đổ." + +#: readelf.c:2820 readelf.c:2888 +#, c-format +msgid "Unrecognized debug option '%s'\n" +msgstr "Không nhận diện tùy chọn gỡ lỗi « %s »\n" + +#: readelf.c:2922 +#, c-format +msgid "Invalid option '-%c'\n" +msgstr "Tùy chọn không hợp lệ « -%c »\n" + +#: readelf.c:2936 +msgid "Nothing to do.\n" +msgstr "Không có gì cần làm.\n" + +#: readelf.c:2948 readelf.c:2964 readelf.c:5906 makeinfo/makeinfo.c:4144 +#: ogg123/cfgfile_options.c:165 ../app/layer_dialog.c:525 +#: ../src/nm-ap-security.c:310 datebook_gui.c:1823 +#, c-format +msgid "none" +msgstr "không có" + +#: readelf.c:2965 +msgid "2's complement, little endian" +msgstr "phần bù của 2, tính trạng cuối nhỏ" + +#: readelf.c:2966 +msgid "2's complement, big endian" +msgstr "phần bù của 2, tính trạng cuối lớn" + +#: readelf.c:2984 +msgid "Not an ELF file - it has the wrong magic bytes at the start\n" +msgstr "" +"Không phải là tập tin ELF — có những byte ma thuật không đúng tại đầu nó.\n" + +#: readelf.c:2992 +#, c-format +msgid "ELF Header:\n" +msgstr "Dòng đầu ELF:\n" + +#: readelf.c:2993 +#, c-format +msgid " Magic: " +msgstr " Ma thuật: " + +#: readelf.c:2997 +#, c-format +msgid " Class: %s\n" +msgstr " Class: %s\n" + +#: readelf.c:2999 +#, c-format +msgid " Data: %s\n" +msgstr " Data: %s\n" + +#: readelf.c:3001 +#, c-format +msgid " Version: %d %s\n" +msgstr " Version: %d %s\n" + +#: readelf.c:3008 +#, c-format +msgid " OS/ABI: %s\n" +msgstr " OS/ABI: %s\n" + +#: readelf.c:3010 +#, c-format +msgid " ABI Version: %d\n" +msgstr " Phiên bản ABI: %d\n" + +#: readelf.c:3012 +#, c-format +msgid " Type: %s\n" +msgstr " Type: %s\n" + +#: readelf.c:3014 +#, c-format +msgid " Machine: %s\n" +msgstr " Machine: %s\n" + +#: readelf.c:3016 +#, c-format +msgid " Version: 0x%lx\n" +msgstr " Version: 0x%lx\n" + +#: readelf.c:3019 +#, c-format +msgid " Entry point address: " +msgstr " Địa chỉ điểm vào : " + +#: readelf.c:3021 +#, c-format +msgid "" +"\n" +" Start of program headers: " +msgstr "" +"\n" +" Điểm đầu các dòng đầu chương trình: " + +#: readelf.c:3023 +#, c-format +msgid "" +" (bytes into file)\n" +" Start of section headers: " +msgstr "" +" (byte vào tập tin)\n" +" Đầu các dòng đầu phần: " + +#: readelf.c:3025 +#, c-format +msgid " (bytes into file)\n" +msgstr " (byte vào tập tin)\n" + +#: readelf.c:3027 +#, c-format +msgid " Flags: 0x%lx%s\n" +msgstr " Flags: 0x%lx%s\n" + +#: readelf.c:3030 +#, c-format +msgid " Size of this header: %ld (bytes)\n" +msgstr " Cỡ phần này: %ld (byte)\n" + +#: readelf.c:3032 +#, c-format +msgid " Size of program headers: %ld (bytes)\n" +msgstr " Cỡ các dòng đầu chương trình: %ld (byte)\n" + +#: readelf.c:3034 +#, c-format +msgid " Number of program headers: %ld\n" +msgstr " Số dòng đầu chương trình: %ld\n" + +#: readelf.c:3036 +#, c-format +msgid " Size of section headers: %ld (bytes)\n" +msgstr " Cỡ các dòng đầu phần: %ld (byte)\n" + +#: readelf.c:3038 +#, c-format +msgid " Number of section headers: %ld" +msgstr " Số dòng đầu phần: %ld" + +#: readelf.c:3043 +#, c-format +msgid " Section header string table index: %ld" +msgstr " Chỉ mục bảng chuỗi dòng đầu phần: %ld" + +#: readelf.c:3074 readelf.c:3107 +msgid "program headers" +msgstr "các dòng đầu chương trình" + +#: readelf.c:3145 readelf.c:3446 readelf.c:3487 readelf.c:3546 readelf.c:3609 +#: readelf.c:3993 readelf.c:4017 readelf.c:5247 readelf.c:5291 readelf.c:5489 +#: readelf.c:6450 readelf.c:6464 readelf.c:11493 readelf.c:11912 +#: readelf.c:11979 src/bus/buses.c:69 src/cmd/include.c:47 src/detect.c:252 +#: src/jtag.c:159 src/jtag.c:270 +msgid "Out of memory\n" +msgstr "Hết bộ nhớ\n" + +#: readelf.c:3172 +#, c-format +msgid "" +"\n" +"There are no program headers in this file.\n" +msgstr "" +"\n" +"Không có dòng đầu chương trình nào trong tập tin này.\n" + +#: readelf.c:3178 +#, c-format +msgid "" +"\n" +"Elf file type is %s\n" +msgstr "" +"\n" +"Kiểu tập tin Elf là %s\n" + +#: readelf.c:3179 +#, c-format +msgid "Entry point " +msgstr "Điểm vào" + +#: readelf.c:3181 +#, c-format +msgid "" +"\n" +"There are %d program headers, starting at offset " +msgstr "" +"\n" +"Có %d dòng đầu chương trình, bắt đầu tại hiệu số" + +#: readelf.c:3193 readelf.c:3195 +#, c-format +msgid "" +"\n" +"Program Headers:\n" +msgstr "" +"\n" +"Dòng đầu chương trình:\n" + +#: readelf.c:3199 +#, c-format +msgid "" +" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n" +msgstr " Kiểu HIệu ĐChỉẢo ĐChỉVật CỡTập CỡNhớ Cờ Cạnh lề\n" + +#: readelf.c:3202 +#, c-format +msgid "" +" Type Offset VirtAddr PhysAddr FileSiz " +"MemSiz Flg Align\n" +msgstr "" +" Kiểu HIệu Địa Chỉ Ảo Địa Chỉ Vật lý CỡTập CỡNhớ Cờ Cạnh lề\n" + +#: readelf.c:3206 +#, c-format +msgid " Type Offset VirtAddr PhysAddr\n" +msgstr " Kiểu HIệu Địa Chỉ Ảo Địa Chỉ Vật lý\n" + +#: readelf.c:3208 +#, c-format +msgid " FileSiz MemSiz Flags Align\n" +msgstr " FileSiz MemSiz Flags Align\n" + +#: readelf.c:3301 +msgid "more than one dynamic segment\n" +msgstr "hơn một phân đoạn động\n" + +#: readelf.c:3312 +msgid "no .dynamic section in the dynamic segment" +msgstr "không có phân đoạn « .dynamic » (động) trong phân đoạn động" + +#: readelf.c:3321 +msgid "the .dynamic section is not contained within the dynamic segment" +msgstr "phần « .dynamic » (động) không có được chứa ở trong phân đoạn động" + +#: readelf.c:3323 +msgid "the .dynamic section is not the first section in the dynamic segment." +msgstr "" +"phần « .dynamic » (động) không phải là phần thứ nhất trong phân đoạn động." + +#: readelf.c:3337 +msgid "Unable to find program interpreter name\n" +msgstr "Không tìm thấy tên bộ giải dịch chương trình\n" + +#: readelf.c:3344 +#, c-format +msgid "" +"\n" +" [Requesting program interpreter: %s]" +msgstr "" +"\n" +" [Đang yêu cầu bộ giải dịch chương trình: %s]" + +#: readelf.c:3356 +#, c-format +msgid "" +"\n" +" Section to Segment mapping:\n" +msgstr "" +"\n" +" Ánh xạ Phần đến Phân đoạn:\n" + +#: readelf.c:3357 +#, c-format +msgid " Segment Sections...\n" +msgstr " Các phần phân đoạn...\n" + +#: readelf.c:3408 +msgid "Cannot interpret virtual addresses without program headers.\n" +msgstr "Không thể giải dịch địa chỉ ảo khi không có dòng đầu chương trình.\n" + +#: readelf.c:3424 +#, c-format +msgid "Virtual address 0x%lx not located in any PT_LOAD segment.\n" +msgstr "" +"Địa chỉ ảo 0x%lx không được định vị trong phân đoạn kiểu « PT_LOAD » nào.\n" + +#: readelf.c:3438 readelf.c:3479 +msgid "section headers" +msgstr "dòng đầu phần" + +#: readelf.c:3523 readelf.c:3586 +msgid "symbols" +msgstr "ký hiệu" + +#: readelf.c:3533 readelf.c:3596 +msgid "symtab shndx" +msgstr "symtab shndx" + +#: readelf.c:3697 readelf.c:3977 +#, c-format +msgid "" +"\n" +"There are no sections in this file.\n" +msgstr "" +"\n" +"Không có phần nào trong tập tin này.\n" + +#: readelf.c:3703 +#, c-format +msgid "There are %d section headers, starting at offset 0x%lx:\n" +msgstr "Có %d dòng đầu phần, bắt đầu tại hiệu số 0x%lx:\n" + +#: readelf.c:3720 readelf.c:4079 readelf.c:4290 readelf.c:4591 readelf.c:5011 +#: readelf.c:6618 +msgid "string table" +msgstr "bảng chuỗi" + +#: readelf.c:3765 +msgid "File contains multiple dynamic symbol tables\n" +msgstr "Tập tin chứa nhiều bảng ký hiệu động\n" + +#: readelf.c:3777 +msgid "File contains multiple dynamic string tables\n" +msgstr "Tập tin chứa nhiều bảng chuỗi động\n" + +#: readelf.c:3782 +msgid "dynamic strings" +msgstr "chuỗi động" + +#: readelf.c:3789 +msgid "File contains multiple symtab shndx tables\n" +msgstr "Tập tin chứa nhiều bảng symtab shndx\n" + +#: readelf.c:3828 +#, c-format +msgid "" +"\n" +"Section Headers:\n" +msgstr "" +"\n" +"Dòng đầu phần:\n" + +#: readelf.c:3830 +#, c-format +msgid "" +"\n" +"Section Header:\n" +msgstr "" +"\n" +"Dòng đầu phần:\n" + +#: readelf.c:3834 +#, c-format +msgid "" +" [Nr] Name Type Addr Off Size ES Flg Lk " +"Inf Al\n" +msgstr "" +" [Nr] Name Type Addr Off Size ES Flg Lk " +"Inf Al\n" + +#: readelf.c:3837 +#, c-format +msgid "" +" [Nr] Name Type Address Off Size ES " +"Flg Lk Inf Al\n" +msgstr "" +" [Nr] Name Type Address Off Size ES " +"Flg Lk Inf Al\n" + +#: readelf.c:3840 +#, c-format +msgid " [Nr] Name Type Address Offset\n" +msgstr " [Nr] Name Type Address Offset\n" + +#: readelf.c:3841 +#, c-format +msgid " Size EntSize Flags Link Info Align\n" +msgstr " Size EntSize Flags Link Info Align\n" + +#: readelf.c:3936 +#, c-format +msgid "" +"Key to Flags:\n" +" W (write), A (alloc), X (execute), M (merge), S (strings)\n" +" I (info), L (link order), G (group), x (unknown)\n" +" O (extra OS processing required) o (OS specific), p (processor specific)\n" +msgstr "" +"Cờ ey \tW\tghi\n" +" \tA\tcấp phát\n" +"\tX\tthực hiện\n" +"\tM\ttrộn\n" +"\tS\tcác chuỗi\n" +"\tI\tthông tin\n" +"\tL\tthứ tự liên kết\n" +"\tG\tnhóm\n" +"\tO\tcần thiết xử lý hệ điều hành thêm\n" +"\to \tđặc trưng cho hệ điều hành\n" +"\ts\tđặc trưng cho bộ xử lý\n" + +#: readelf.c:3954 +#, c-format +msgid "[: 0x%x]" +msgstr "[: 0x%x]" + +#: readelf.c:3984 +msgid "Section headers are not available!\n" +msgstr "Dòng đầu phần không sẵn sàng.\n" + +#: readelf.c:4008 +#, c-format +msgid "" +"\n" +"There are no section groups in this file.\n" +msgstr "" +"\n" +"Không có nhóm phần nào trong tập tin này.\n" + +#: readelf.c:4042 +#, c-format +msgid "Bad sh_link in group section `%s'\n" +msgstr "Có liên kết « sh_link » sai trong phần nhóm « %s »\n" + +#: readelf.c:4061 +#, c-format +msgid "Bad sh_info in group section `%s'\n" +msgstr "Có thông tin « sh_info » sai trong phần nhóm « %s »\n" + +#: readelf.c:4085 readelf.c:6947 +msgid "section data" +msgstr "dữ liệu phần" + +#: readelf.c:4097 +#, c-format +msgid " [Index] Name\n" +msgstr " [Chỉ mục] Tên\n" + +#: readelf.c:4114 +#, c-format +msgid "section [%5u] already in group section [%5u]\n" +msgstr "phần [%5u] đã có trong phần nhóm [%5u]\n" + +#: readelf.c:4127 +#, c-format +msgid "section 0 in group section [%5u]\n" +msgstr "phần 0 trong phần nhóm [%5u]\n" + +#: readelf.c:4224 +#, c-format +msgid "" +"\n" +"'%s' relocation section at offset 0x%lx contains %ld bytes:\n" +msgstr "" +"\n" +"phần định vị lại « %s » tại hiệu số 0x%lx chứa %ld byte:\n" + +#: readelf.c:4236 +#, c-format +msgid "" +"\n" +"There are no dynamic relocations in this file.\n" +msgstr "" +"\n" +"Không có việc định vị lại động nào trong tập tin này.\n" + +#: readelf.c:4260 +#, c-format +msgid "" +"\n" +"Relocation section " +msgstr "" +"\n" +"Phần định vị lại" + +#: readelf.c:4265 readelf.c:4666 readelf.c:4680 readelf.c:5025 +#, c-format +msgid "'%s'" +msgstr "« %s »" + +#: readelf.c:4267 readelf.c:4682 readelf.c:5027 +#, c-format +msgid " at offset 0x%lx contains %lu entries:\n" +msgstr " tại hiệu số 0x%lx chứa %lu mục nhập:\n" + +#: readelf.c:4308 +#, c-format +msgid "" +"\n" +"There are no relocations in this file.\n" +msgstr "" +"\n" +"Không có việc định vị lại nào trong tập tin này.\n" + +#: readelf.c:4482 readelf.c:4862 +msgid "unwind table" +msgstr "tri ra bảng" + +#: readelf.c:4540 readelf.c:4959 +#, c-format +msgid "Skipping unexpected relocation type %s\n" +msgstr "Đang nhảy kiểu định vị lại bất ngờ %s\n" + +#: readelf.c:4598 readelf.c:5018 readelf.c:5069 +#, c-format +msgid "" +"\n" +"There are no unwind sections in this file.\n" +msgstr "" +"\n" +"Không có phần tri ra nào trong tập tin này.\n" + +#: readelf.c:4661 +#, c-format +msgid "" +"\n" +"Could not find unwind info section for " +msgstr "" +"\n" +"Không thể tìm thấy phần thông tin tri ra cho " + +#: readelf.c:4673 +msgid "unwind info" +msgstr "thông tin tri ra" + +#: readelf.c:4675 readelf.c:5024 +#, c-format +msgid "" +"\n" +"Unwind section " +msgstr "" +"\n" +"Phần tri ra " + +#: readelf.c:5228 readelf.c:5272 +msgid "dynamic section" +msgstr "phần động" + +#: readelf.c:5349 +#, c-format +msgid "" +"\n" +"There is no dynamic section in this file.\n" +msgstr "" +"\n" +"Không có phần động nào trong tập tin này.\n" + +#: readelf.c:5387 +msgid "Unable to seek to end of file!" +msgstr "• Không thể tìm tới kết thúc tập tin. •" + +#: readelf.c:5400 +msgid "Unable to determine the number of symbols to load\n" +msgstr "Không thể quyết định số ký hiệu cần tải\n" + +#: readelf.c:5435 +msgid "Unable to seek to end of file\n" +msgstr "Không thể tìm tới kết thúc tập tin\n" + +#: readelf.c:5442 +msgid "Unable to determine the length of the dynamic string table\n" +msgstr "Không thể quyết định độ dài của bảng chuỗi động\n" + +#: readelf.c:5447 +msgid "dynamic string table" +msgstr "bảng chuỗi động" + +#: readelf.c:5482 +msgid "symbol information" +msgstr "thông tin ký hiệu" + +#: readelf.c:5507 +#, c-format +msgid "" +"\n" +"Dynamic section at offset 0x%lx contains %u entries:\n" +msgstr "" +"\n" +"Phần động tại hiệu số 0x%lx chứa %u mục nhập:\n" + +#: readelf.c:5510 +#, c-format +msgid " Tag Type Name/Value\n" +msgstr " Thẻ Kiểu Tên/Giá trị\n" + +#: readelf.c:5546 +#, c-format +msgid "Auxiliary library" +msgstr "Thư viên phụ" + +#: readelf.c:5550 +#, c-format +msgid "Filter library" +msgstr "Thư viên lọc" + +#: readelf.c:5554 src/glade_options.c:191 +#, c-format +msgid "Configuration file" +msgstr "Tập tin cấu hình" + +#: readelf.c:5558 +#, c-format +msgid "Dependency audit library" +msgstr "Thư viên kiểm tra cách phụ thuộc" + +#: readelf.c:5562 +#, c-format +msgid "Audit library" +msgstr "Thư viên kiểm tra" + +#: readelf.c:5580 readelf.c:5608 readelf.c:5636 cardinfo.c:1401 +#: ../gmedia_slice/interface.c:416 +#, c-format +msgid "Flags:" +msgstr "Cờ :" + +#: readelf.c:5583 readelf.c:5611 readelf.c:5638 +#, c-format +msgid " None\n" +msgstr " Không có\n" + +#: readelf.c:5759 +#, c-format +msgid "Shared library: [%s]" +msgstr "Thư viện dùng chung: [%s]" + +#: readelf.c:5762 +#, c-format +msgid " program interpreter" +msgstr " bộ giải dịch chương trình" + +#: readelf.c:5766 +#, c-format +msgid "Library soname: [%s]" +msgstr "soname (tên so) thư viên: [%s]" + +#: readelf.c:5770 +#, c-format +msgid "Library rpath: [%s]" +msgstr "rpath (đường dẫn r) thư viên: [%s]" + +#: readelf.c:5774 +#, c-format +msgid "Library runpath: [%s]" +msgstr "runpath (đường dẫn chạy) thư viên: [%s]" + +#: readelf.c:5837 +#, c-format +msgid "Not needed object: [%s]\n" +msgstr "Đối tượng không cần thiết: [%s]\n" + +#: readelf.c:5951 +#, c-format +msgid "" +"\n" +"Version definition section '%s' contains %ld entries:\n" +msgstr "" +"\n" +"Phần định nghĩa phiên bản « %s » chứa %ld mục nhập:\n" + +#: readelf.c:5954 +#, c-format +msgid " Addr: 0x" +msgstr " ĐChỉ: 0x" + +#: readelf.c:5956 readelf.c:6148 +#, c-format +msgid " Offset: %#08lx Link: %lx (%s)\n" +msgstr " HIệu : %#08lx LKết: %lx (%s)\n" + +#: readelf.c:5961 +msgid "version definition section" +msgstr "phần định nghĩa phiên bản" + +#: readelf.c:5987 +#, c-format +msgid " %#06x: Rev: %d Flags: %s" +msgstr " %#06x: Bản: %d Cờ: %s" + +#: readelf.c:5990 +#, c-format +msgid " Index: %d Cnt: %d " +msgstr " Chỉ mục: %d Đếm: %d " + +#: readelf.c:6001 schroot/sbuild-chroot.c:392 ../mimedir/mimedir-vcard.c:3409 +#, c-format +msgid "Name: %s\n" +msgstr "Tên: %s\n" + +#: readelf.c:6003 +#, c-format +msgid "Name index: %ld\n" +msgstr "Chỉ mục tên: %ld\n" + +#: readelf.c:6018 +#, c-format +msgid " %#06x: Parent %d: %s\n" +msgstr " %#06x: Mẹ %d: %s\n" + +#: readelf.c:6021 +#, c-format +msgid " %#06x: Parent %d, name index: %ld\n" +msgstr " %#06x: Mẹ %d, chỉ mục tên: %ld\n" + +#: readelf.c:6040 +#, c-format +msgid "" +"\n" +"Version needs section '%s' contains %ld entries:\n" +msgstr "" +"\n" +"Phần cần thiết phiên bản « %s » chứa %ld mục nhập:\n" + +#: readelf.c:6043 +#, c-format +msgid " Addr: 0x" +msgstr " ĐChỉ: 0x" + +#: readelf.c:6045 +#, c-format +msgid " Offset: %#08lx Link to section: %ld (%s)\n" +msgstr " HIệu : %#08lx Liên kết đến phần: %ld (%s)\n" + +#: readelf.c:6050 +msgid "version need section" +msgstr "phần cần phiên bản" + +#: readelf.c:6072 +#, c-format +msgid " %#06x: Version: %d" +msgstr " %#06x: PhBản: %d" + +#: readelf.c:6075 +#, c-format +msgid " File: %s" +msgstr " Tập tin: %s" + +#: readelf.c:6077 +#, c-format +msgid " File: %lx" +msgstr " Tập tin: %lx" + +#: readelf.c:6079 +#, c-format +msgid " Cnt: %d\n" +msgstr " Đếm: %d\n" + +#: readelf.c:6097 +#, c-format +msgid " %#06x: Name: %s" +msgstr " %#06x: Tên: %s" + +#: readelf.c:6100 +#, c-format +msgid " %#06x: Name index: %lx" +msgstr " %#06x: Chỉ mục tên: %lx" + +#: readelf.c:6103 +#, c-format +msgid " Flags: %s Version: %d\n" +msgstr " Cờ: %s Phiên bản: %d\n" + +#: readelf.c:6139 +msgid "version string table" +msgstr "bảng chuỗi phiên bản" + +#: readelf.c:6143 +#, c-format +msgid "" +"\n" +"Version symbols section '%s' contains %d entries:\n" +msgstr "" +"\n" +"Phần ký hiệu phiên bản « %s » chứa %d mục nhập:\n" + +#: readelf.c:6146 +#, c-format +msgid " Addr: " +msgstr " ĐChỉ: " + +#: readelf.c:6156 +msgid "version symbol data" +msgstr "dữ liệu ký hiệu phiên bản" + +#: readelf.c:6183 +msgid " 0 (*local*) " +msgstr " 0 (*local*) " + +#: readelf.c:6187 +msgid " 1 (*global*) " +msgstr " 1 (*toàn cụcglobal*) " + +#: readelf.c:6223 readelf.c:6677 +msgid "version need" +msgstr "phiên bản cần" + +#: readelf.c:6233 +msgid "version need aux (2)" +msgstr "phiên bản cần phụ (2)" + +#: readelf.c:6275 readelf.c:6740 +msgid "version def" +msgstr "phbản đ.nghĩa" + +#: readelf.c:6294 readelf.c:6755 +msgid "version def aux" +msgstr "phbản đ.nghĩa phụ" + +#: readelf.c:6325 +#, c-format +msgid "" +"\n" +"No version information found in this file.\n" +msgstr "" +"\n" +"Không tìm thấy thông tin phiên bản trong tập tin này.\n" + +#: readelf.c:6456 +msgid "Unable to read in dynamic data\n" +msgstr "Không thể đọc vào dữ liệu động\n" + +#: readelf.c:6509 +msgid "Unable to seek to start of dynamic information" +msgstr "Không thể tìm tới đầu thông tin động" + +#: readelf.c:6515 +msgid "Failed to read in number of buckets\n" +msgstr "Việc đọc vào số xô bị lỗi\n" + +#: readelf.c:6521 +msgid "Failed to read in number of chains\n" +msgstr "Việc đọc vào số dây bị lỗi\n" + +#: readelf.c:6541 +#, c-format +msgid "" +"\n" +"Symbol table for image:\n" +msgstr "" +"\n" +"Bảng ký hiệu cho ảnh:\n" + +#: readelf.c:6543 +#, c-format +msgid " Num Buc: Value Size Type Bind Vis Ndx Name\n" +msgstr " Số xô : Giá trị Cỡ Kiểu Trộn Hiện Ndx Tên\n" + +#: readelf.c:6545 +#, c-format +msgid " Num Buc: Value Size Type Bind Vis Ndx Name\n" +msgstr " Số xô : Giá trị Cỡ Kiểu Trộn Hiện Ndx Tên\n" + +#: readelf.c:6597 +#, c-format +msgid "" +"\n" +"Symbol table '%s' contains %lu entries:\n" +msgstr "" +"\n" +"Bảng ký hiệu « %s » chứa %lu mục nhập:\n" + +#: readelf.c:6601 +#, c-format +msgid " Num: Value Size Type Bind Vis Ndx Name\n" +msgstr " Số : Giá trị Cỡ Kiểu Trộn Hiện Ndx Tên\n" + +#: readelf.c:6603 +#, c-format +msgid " Num: Value Size Type Bind Vis Ndx Name\n" +msgstr " Số : Giá trị Cỡ Kiểu Trộn Hiện Ndx Tên\n" + +#: readelf.c:6649 +msgid "version data" +msgstr "dữ liệu phiên bản" + +#: readelf.c:6690 +msgid "version need aux (3)" +msgstr "phiên bản cần phụ (3)" + +#: readelf.c:6715 +msgid "bad dynamic symbol" +msgstr "ký hiệu động sai" + +#: readelf.c:6778 +#, c-format +msgid "" +"\n" +"Dynamic symbol information is not available for displaying symbols.\n" +msgstr "" +"\n" +"Không có thông tin ký hiệu động để hiển thị ký hiệu.\n" + +#: readelf.c:6790 +#, c-format +msgid "" +"\n" +"Histogram for bucket list length (total of %lu buckets):\n" +msgstr "" +"\n" +"Biểu đồ tần xuất cho độ dài danh sách xô (tổng số %lu xô):\n" + +#: readelf.c:6792 +#, c-format +msgid " Length Number %% of total Coverage\n" +msgstr " Dài Số %% tổng phạm vị\n" + +#: readelf.c:6797 readelf.c:6813 readelf.c:10967 readelf.c:11159 +#: libgphoto2_port/gphoto2-port-result.c:76 +#: ../camel/camel-tcp-stream-openssl.c:595 ../src/yelp-toc-pager.c:1049 +#: ../src/yelp-xslt-pager.c:382 address.c:288 address.c:552 alarms.c:352 +#: dat.c:181 dat.c:655 dat.c:857 dat.c:991 dat.c:1130 datebook.c:96 +#: datebook.c:397 datebook.c:404 datebook.c:434 datebook.c:1053 jpilot.c:1534 +#: libplugin.c:467 libplugin.c:646 libplugin.c:753 libplugin.c:832 +#: libplugin.c:873 memo.c:95 memo.c:367 plugins.c:108 prefs.c:314 prefs.c:339 +#: prefs.c:854 sync.c:252 sync.c:1153 sync.c:2320 todo.c:206 todo.c:548 +#: utils.c:2307 +msgid "Out of memory" +msgstr "Hết bộ nhớ" + +#: readelf.c:6862 +#, c-format +msgid "" +"\n" +"Dynamic info segment at offset 0x%lx contains %d entries:\n" +msgstr "" +"\n" +"Phân đoạn thông tin động tại hiệu số 0x%lx chứa %d mục nhập:\n" + +#: readelf.c:6865 +#, c-format +msgid " Num: Name BoundTo Flags\n" +msgstr " Số : Tên ĐóngVới Cờ\n" + +#: readelf.c:6917 +#, c-format +msgid "" +"\n" +"Assembly dump of section %s\n" +msgstr "" +"\n" +"Việc đổ thanh ghi của phần %s\n" + +#: readelf.c:6938 +#, c-format +msgid "" +"\n" +"Section '%s' has no data to dump.\n" +msgstr "" +"\n" +"Phần « %s » không có dữ liệu cần đổ.\n" + +#: readelf.c:6943 +#, c-format +msgid "" +"\n" +"Hex dump of section '%s':\n" +msgstr "" +"\n" +"Việc đổ thập lục của phần « %s »:\n" + +#: readelf.c:7090 +msgid "badly formed extended line op encountered!\n" +msgstr "gặp thao tác dòng đã mở rộng dạng sai.\n" + +#: readelf.c:7097 +#, c-format +msgid " Extended opcode %d: " +msgstr " Opcode (mã thao tác) đã mở rộng %d: " + +#: readelf.c:7102 +#, c-format +msgid "" +"End of Sequence\n" +"\n" +msgstr "" +"Kết thúc dãy\n" +"\n" + +#: readelf.c:7108 +#, c-format +msgid "set Address to 0x%lx\n" +msgstr "đặt Địa chỉ là 0x%lx\n" + +#: readelf.c:7113 +#, c-format +msgid " define new File Table entry\n" +msgstr " định nghĩa mục nhập Bảng Tập tin mới\n" + +#: readelf.c:7114 readelf.c:9032 +#, c-format +msgid " Entry\tDir\tTime\tSize\tName\n" +msgstr " Mục\tTMục\tGiờ\tCỡ\tTên\n" + +# Variable: don't translate / Biến: đừng dịch +#: readelf.c:7116 +#, c-format +msgid " %d\t" +msgstr " %d\t" + +# Variable: don't translate / Biến: đừng dịch +#: readelf.c:7119 readelf.c:7121 readelf.c:7123 readelf.c:9044 readelf.c:9046 +#: readelf.c:9048 +#, c-format +msgid "%lu\t" +msgstr "%lu\t" + +# Variable: do not translate/ biến: đừng dịch +#: readelf.c:7124 +#, c-format +msgid "" +"%s\n" +"\n" +msgstr "" +"%s\n" +"\n" + +#: readelf.c:7128 +#, c-format +msgid "UNKNOWN: length %d\n" +msgstr "KHÔNG RÕ: độ dài %d\n" + +#: readelf.c:7155 +msgid "debug_str section data" +msgstr "debug_str section data" + +#: readelf.c:7173 +msgid "" +msgstr "" + +#: readelf.c:7176 +msgid "" +msgstr "" + +#: readelf.c:7201 +msgid "debug_loc section data" +msgstr "dữ liệu phần « debug_loc » (định vị gỡ lỗi)" + +#: readelf.c:7235 +msgid "debug_range section data" +msgstr "dữ liệu phần « debug_range » (phạm vị gỡ lỗi)" + +#: readelf.c:7307 +#, c-format +msgid "" +"%s: skipping unexpected symbol type %s in relocation in section .rela%s\n" +msgstr "" +"%s: đang nhảy qua kiểu ký hiệu bất ngờ %s trong việc định vị lại trong phần ." +"rela%s\n" + +#: readelf.c:7321 +#, c-format +msgid "skipping unexpected symbol type %s in relocation in section .rela.%s\n" +msgstr "" +"đang nhảy qua kiểu ký hiệu bất ngờ %s trong việc định vị lại trong phần .rela" +"%s\n" + +#: readelf.c:7565 +#, c-format +msgid "Unknown TAG value: %lx" +msgstr "Giá trị TAG (thẻ) không rõ : %lx" + +#: readelf.c:7601 +#, c-format +msgid "Unknown FORM value: %lx" +msgstr "Giá trị FORM (dạng) không rõ : %lx" + +#: readelf.c:7610 +#, c-format +msgid " %lu byte block: " +msgstr " Khối %lu byte: " + +#: readelf.c:7944 +#, c-format +msgid "(User defined location op)" +msgstr "(Thao tác định vị do người dùng định nghĩa)" + +#: readelf.c:7946 +#, c-format +msgid "(Unknown location op)" +msgstr "(Thao tác định vị không rõ)" + +#: readelf.c:8015 +msgid "Internal error: DWARF version is not 2 or 3.\n" +msgstr "Lỗi nội bộ: phiên bản DWARF không phải là 2 hay 3.\n" + +#: readelf.c:8113 +msgid "DW_FORM_data8 is unsupported when sizeof (unsigned long) != 8\n" +msgstr "" +"Không hỗ trợ « DW_FORM_data8 » khi « sizeof (unsigned long) != 8 » [kích cỡ " +"của (dài không ký)]\n" + +#: readelf.c:8162 +#, c-format +msgid " (indirect string, offset: 0x%lx): %s" +msgstr " (chuỗi gián tiếp, hiệu số: 0x%lx): %s" + +#: readelf.c:8171 +#, c-format +msgid "Unrecognized form: %d\n" +msgstr "Không nhận diện dạng: %d\n" + +#: readelf.c:8256 +#, c-format +msgid "(not inlined)" +msgstr "(không đặt trực tiếp)" + +#: readelf.c:8259 +#, c-format +msgid "(inlined)" +msgstr "(đặt trực tiếp)" + +#: readelf.c:8262 +#, c-format +msgid "(declared as inline but ignored)" +msgstr "(khai báo là trực tiếp mà bị bỏ qua)" + +#: readelf.c:8265 +#, c-format +msgid "(declared as inline and inlined)" +msgstr "(khai báo là trực tiếp và đặt trực tiếp)" + +#: readelf.c:8268 +#, c-format +msgid " (Unknown inline attribute value: %lx)" +msgstr " (Không biết giá trị thuộc tính trực tiếp: %lx)" + +#: readelf.c:8413 readelf.c:9537 +#, c-format +msgid " [without DW_AT_frame_base]" +msgstr " [không có DW_AT_frame_base (cơ bản khung)]" + +#: readelf.c:8416 +#, c-format +msgid "(location list)" +msgstr "(danh sách địa điểm)" + +#: readelf.c:8534 +#, c-format +msgid "Unknown AT value: %lx" +msgstr "Không biết giá trị AT: %lx" + +#: readelf.c:8602 +msgid "No comp units in .debug_info section ?" +msgstr "" +"Không có đơn vị biên dịch trong phần « .debug_info » (thông tin gỡ lỗi) ?" + +#: readelf.c:8611 +#, c-format +msgid "Not enough memory for a debug info array of %u entries" +msgstr "Không đủ bộ nhớ cho mảng thông tin gỡ lỗi có mục nhập %u" + +#: readelf.c:8619 readelf.c:9630 +#, c-format +msgid "" +"The section %s contains:\n" +"\n" +msgstr "" +"Phần %s chứa:\n" +"\n" + +#: readelf.c:8693 +#, c-format +msgid " Compilation Unit @ %lx:\n" +msgstr " Đơn vị biên dịch @ %lx:\n" + +#: readelf.c:8694 +#, c-format +msgid " Length: %ld\n" +msgstr " Dài: %ld\n" + +#: readelf.c:8695 +#, c-format +msgid " Version: %d\n" +msgstr " Phiên bản: %d\n" + +#: readelf.c:8696 +#, c-format +msgid " Abbrev Offset: %ld\n" +msgstr " Hiệu số tắt: %ld\n" + +#: readelf.c:8697 +#, c-format +msgid " Pointer Size: %d\n" +msgstr " Cỡ con trỏ : %d\n" + +#: readelf.c:8702 +msgid "Only version 2 and 3 DWARF debug information is currently supported.\n" +msgstr "Hỗ trợ chỉ thông tin gỡ lỗi phiên bản DWARF 2 và 3 thôi.\n" + +#: readelf.c:8717 +msgid "Unable to locate .debug_abbrev section!\n" +msgstr "Không thể định vị phần « .debug_abbrev » (gỡ lỗi viết tắt)\n" + +#: readelf.c:8722 +msgid "debug_abbrev section data" +msgstr "dữ liệu phần « .debug_abbrev » (gỡ lỗi viết tắt)" + +#: readelf.c:8759 +#, c-format +msgid "Unable to locate entry %lu in the abbreviation table\n" +msgstr "Không thể định vị mục nhâp %lu trong bảng viết tắt\n" + +#: readelf.c:8765 +#, c-format +msgid " <%d><%lx>: Abbrev Number: %lu (%s)\n" +msgstr " <%d><%lx>: Số viết tắt: %lu (%s)\n" + +#: readelf.c:8838 +#, c-format +msgid "%s section needs a populated .debug_info section\n" +msgstr "Phần %s cần phần « .debug_info » (thông tin gỡ lỗi) có dữ liệu\n" + +#: readelf.c:8845 +#, c-format +msgid "%s section has more comp units than .debug_info section\n" +msgstr "" +"Phần %s có nhiều đơn vị biên dịch hơn phần « .debug_info » (thông tin gỡ " +"lỗi)\n" + +#: readelf.c:8847 +#, c-format +msgid "" +"assuming that the pointer size is %d, from the last comp unit in ." +"debug_info\n" +"\n" +msgstr "" +"giả sử kích cỡ con trỏ là %d, từ đơn vị biên dịch cuối cùng trong « ." +"debug_info » (thông tin gỡ lỗi)\n" +"\n" + +#: readelf.c:8891 +msgid "extracting information from .debug_info section" +msgstr "đang trích thông tin ra phần « .debug_info » (thông tin gỡ lỗi)" + +#: readelf.c:8909 +#, c-format +msgid "" +"\n" +"Dump of debug contents of section %s:\n" +"\n" +msgstr "" +"\n" +"Việc đổ nội dung gỡ lỗi của phần %s:\n" + +#: readelf.c:8948 +msgid "The line info appears to be corrupt - the section is too small\n" +msgstr "Hình như dòng bị hỏng — phần quá nhỏ\n" + +#: readelf.c:8957 +msgid "Only DWARF version 2 and 3 line info is currently supported.\n" +msgstr "Hỗ trợ hiện thời chỉ thông tin dòng DWARF phiên bản 2 và 3.\n" + +#: readelf.c:8984 +#, c-format +msgid " Length: %ld\n" +msgstr " Dài: %ld\n" + +#: readelf.c:8985 +#, c-format +msgid " DWARF Version: %d\n" +msgstr " Phiên bản DWARF: %d\n" + +#: readelf.c:8986 +#, c-format +msgid " Prologue Length: %d\n" +msgstr " Dài đoạn mở đầu : %d\n" + +#: readelf.c:8987 +#, c-format +msgid " Minimum Instruction Length: %d\n" +msgstr " Dài câu lệnh tối thiểu : %d\n" + +#: readelf.c:8988 +#, c-format +msgid " Initial value of 'is_stmt': %d\n" +msgstr " Giá trị đầu của « is_stmt »: %d\n" + +#: readelf.c:8989 +#, c-format +msgid " Line Base: %d\n" +msgstr " Cơ bản dòng: %d\n" + +#: readelf.c:8990 +#, c-format +msgid " Line Range: %d\n" +msgstr " Phạm vị dòng: %d\n" + +#: readelf.c:8991 +#, c-format +msgid " Opcode Base: %d\n" +msgstr " Cơ bản mã thao tác: %d\n" + +#: readelf.c:8992 +#, c-format +msgid " (Pointer size: %u)\n" +msgstr " (cỡ con trỏ : %u)\n" + +#: readelf.c:9001 +#, c-format +msgid "" +"\n" +" Opcodes:\n" +msgstr "" +"\n" +" Mã thao tác:\n" + +#: readelf.c:9004 +#, c-format +msgid " Opcode %d has %d args\n" +msgstr " Mã thao tác %d có %d đối số\n" + +#: readelf.c:9010 +#, c-format +msgid "" +"\n" +" The Directory Table is empty.\n" +msgstr "" +"\n" +" Bảng Thư mục rỗng\n" + +#: readelf.c:9013 +#, c-format +msgid "" +"\n" +" The Directory Table:\n" +msgstr "" +"\n" +" Bảng Thư mục:\n" + +# Variable: don't translate / Biến: đừng dịch +#: readelf.c:9017 +#, c-format +msgid " %s\n" +msgstr " %s\n" + +#: readelf.c:9028 +#, c-format +msgid "" +"\n" +" The File Name Table is empty.\n" +msgstr "" +"\n" +" Bảng Tên Tập tin rỗng:\n" + +#: readelf.c:9031 +#, c-format +msgid "" +"\n" +" The File Name Table:\n" +msgstr "" +"\n" +" Bảng Tên Tập tin:\n" + +# Variable: don't translate / Biến: đừng dịch +#: readelf.c:9039 +#, c-format +msgid " %d\t" +msgstr " %d\t" + +# Variable: do not translate/ biến: đừng dịch +#: readelf.c:9050 src/po-charset.c:298 src/po-charset.c:323 +#: src/po-charset.c:311 src/po-charset.c:336 src/cmd/dr.c:79 +#, c-format +msgid "%s\n" +msgstr "%s\n" + +#. Now display the statements. +#: readelf.c:9058 +#, c-format +msgid "" +"\n" +" Line Number Statements:\n" +msgstr "" +"\n" +" Câu Số thứ tự Dòng:\n" + +#: readelf.c:9073 +#, c-format +msgid " Special opcode %d: advance Address by %d to 0x%lx" +msgstr " Mã thao tác đặc biệt %d: nâng cao Địa chỉ bước %d tới 0x%lx" + +#: readelf.c:9077 +#, c-format +msgid " and Line by %d to %d\n" +msgstr " và Dòng bước %d tới %d\n" + +#: readelf.c:9088 +#, c-format +msgid " Copy\n" +msgstr " Chép\n" + +#: readelf.c:9095 +#, c-format +msgid " Advance PC by %d to %lx\n" +msgstr " Nâng cao PC bước %d tới %lx\n" + +#: readelf.c:9103 +#, c-format +msgid " Advance Line by %d to %d\n" +msgstr " Nâng cao dòng bước %d tới %d\n" + +#: readelf.c:9110 +#, c-format +msgid " Set File Name to entry %d in the File Name Table\n" +msgstr " Lập Tên Tập tin là mục nhập %d trong Bảng Tên Tập tin\n" + +#: readelf.c:9118 +#, c-format +msgid " Set column to %d\n" +msgstr " Lập cột là %d\n" + +#: readelf.c:9125 +#, c-format +msgid " Set is_stmt to %d\n" +msgstr " Lập « is_stmt » (là câu) là %d\n" + +#: readelf.c:9130 +#, c-format +msgid " Set basic block\n" +msgstr " Lập khối cơ bản\n" + +#: readelf.c:9138 +#, c-format +msgid " Advance PC by constant %d to 0x%lx\n" +msgstr " Nâng cao PC bước hằng số %d tới 0x%lx\n" + +#: readelf.c:9146 +#, c-format +msgid " Advance PC by fixed size amount %d to 0x%lx\n" +msgstr " Nâng cao PC bước kích cỡ cố định %d tới 0x%lx\n" + +#: readelf.c:9151 +#, c-format +msgid " Set prologue_end to true\n" +msgstr " Lập « prologue_end » (kết thúc đoạn mở đầu) là true (đúng)\n" + +#: readelf.c:9155 +#, c-format +msgid " Set epilogue_begin to true\n" +msgstr " Lập « epilogue_begin » (đầu phần kết) là true (đúng)\n" + +#: readelf.c:9161 +#, c-format +msgid " Set ISA to %d\n" +msgstr " Lập ISA là %d\n" + +#: readelf.c:9165 +#, c-format +msgid " Unknown opcode %d with operands: " +msgstr " Gặp opcode (mã thao tác) không rõ %d với tác tử : " + +#: readelf.c:9193 readelf.c:9279 readelf.c:9354 +#, c-format +msgid "" +"Contents of the %s section:\n" +"\n" +msgstr "" +"Nội dung của phần %s:\n" +"\n" + +#: readelf.c:9233 +msgid "Only DWARF 2 and 3 pubnames are currently supported\n" +msgstr "Hỗ trợ hiện thời chỉ pubnames (tên công) DWARF phiên bản 2 và 3 thôi\n" + +#: readelf.c:9240 +#, c-format +msgid " Length: %ld\n" +msgstr " Length: %ld\n" + +#: readelf.c:9242 +#, c-format +msgid " Version: %d\n" +msgstr " Version: %d\n" + +#: readelf.c:9244 +#, c-format +msgid " Offset into .debug_info section: %ld\n" +msgstr "" +" Hiệu số vào phầnO« ffset into .» (thông tin gỡ lỗi)nfo section: %ld\n" + +#: readelf.c:9246 +#, c-format +msgid " Size of area in .debug_info section: %ld\n" +msgstr "" +" Kích cỡ của vùng trong phần « .debug_info » (thông tin gỡ lỗi): %ld\n" + +#: readelf.c:9249 +#, c-format +msgid "" +"\n" +" Offset\tName\n" +msgstr "" +"\n" +" Hiệu\tTên\n" + +#: readelf.c:9300 +#, c-format +msgid " DW_MACINFO_start_file - lineno: %d filenum: %d\n" +msgstr "" +" DW_MACINFO_start_file (bắt đầu tập tin) — số_dòng: %d số_tập_tin: %d\n" + +#: readelf.c:9306 +#, c-format +msgid " DW_MACINFO_end_file\n" +msgstr " DW_MACINFO_end_file (kết thúc tập tin)\n" + +#: readelf.c:9314 +#, c-format +msgid " DW_MACINFO_define - lineno : %d macro : %s\n" +msgstr " DW_MACINFO_define (định nghĩa) — số_dòng : %d bộ_lệnh : %s\n" + +#: readelf.c:9323 +#, c-format +msgid " DW_MACINFO_undef - lineno : %d macro : %s\n" +msgstr " DW_MACINFO_undef (chưa định nghĩa) — số_dòng : %d bộ_lệnh : %s\n" + +#: readelf.c:9335 +#, c-format +msgid " DW_MACINFO_vendor_ext - constant : %d string : %s\n" +msgstr "" +" DW_MACINFO_vendor_ext (phần mở rộng nhà bán) — hằng số : %d chuối : %s\n" + +#: readelf.c:9363 +#, c-format +msgid " Number TAG\n" +msgstr " Số THẺ\n" + +# Variable: don't translate / Biến: đừng dịch +#: readelf.c:9369 +#, c-format +msgid " %ld %s [%s]\n" +msgstr " %ld %s [%s]\n" + +#: readelf.c:9372 +msgid "has children" +msgstr "có điều con" + +#: readelf.c:9372 ../srcore/srctrl.c:1036 +msgid "no children" +msgstr "không có con" + +# Variable: don't translate / Biến: đừng dịch +#: readelf.c:9375 +#, c-format +msgid " %-18s %s\n" +msgstr " %-18s %s\n" + +#: readelf.c:9410 +msgid "" +"\n" +"The .debug_loc section is empty.\n" +msgstr "" +"\n" +"Phần « .debug_loc » (gỡ lỗi định vị) rỗng:\n" + +#. FIXME: Should we handle this case? +#: readelf.c:9455 +msgid "Location lists in .debug_info section aren't in ascending order!\n" +msgstr "" +"• Các danh sách địa điểm trong phần « .debug_info » (thông tin gỡ lỗi) không " +"phải theo thứ tự dần. •\n" + +#: readelf.c:9458 +msgid "No location lists in .debug_info section!\n" +msgstr "" +"• Không có danh sách địa điểm trong phần « .debug_info » (thông tin gỡ lỗi). " +"•\n" + +#: readelf.c:9461 +#, c-format +msgid "Location lists in .debug_loc section start at 0x%lx\n" +msgstr "" +"Danh sách địa điểm trong phần « .debug_info » (thông tin gỡ lỗi) bắt đầu tại " +"0x%lx\n" + +#: readelf.c:9464 +#, c-format +msgid "" +"Contents of the .debug_loc section:\n" +"\n" +msgstr "" +"Nội dung của phần « .debug_info » (thông tin gỡ lỗi):\n" +"\n" + +#: readelf.c:9465 +#, c-format +msgid " Offset Begin End Expression\n" +msgstr " HIệu Đầu Cuối Biểu thức\n" + +#: readelf.c:9495 +#, c-format +msgid "There is a hole [0x%lx - 0x%lx] in .debug_loc section.\n" +msgstr "" +"Có một lỗ [0x%lx - 0x%lx] trong phần « .debug_info » (thông tin gỡ lỗi).\n" + +#: readelf.c:9498 +#, c-format +msgid "There is an overlap [0x%lx - 0x%lx] in .debug_loc section.\n" +msgstr "" +"Có một nơi chồng lấp [0x%lx - 0x%lx] trong phần « .debug_info » (thông tin " +"gỡ lỗi).\n" + +#: readelf.c:9512 readelf.c:9837 +#, c-format +msgid " %8.8lx \n" +msgstr " %8.8lx \n" + +#: readelf.c:9540 readelf.c:9854 +msgid " (start == end)" +msgstr " (start == end)" + +#: readelf.c:9542 readelf.c:9856 +msgid " (start > end)" +msgstr " (start > end)" + +#: readelf.c:9566 +#, c-format +msgid "" +"\n" +"The .debug_str section is empty.\n" +msgstr "" +"\n" +"Phần « .debug_str » (chuỗi gỡ lỗi) rỗng.\n" + +#: readelf.c:9570 +#, c-format +msgid "" +"Contents of the .debug_str section:\n" +"\n" +msgstr "" +"Nội dung của phần « .debug_str » (chuỗi gỡ lỗi):\n" +"\n" + +#: readelf.c:9675 +msgid "Only DWARF 2 and 3 aranges are currently supported.\n" +msgstr "Hỗ trợ hiện thời chỉ arange (phạm vị a) DWARF phiên bản 2 và 3 thôi.\n" + +#: readelf.c:9679 +#, c-format +msgid " Length: %ld\n" +msgstr " Dài: %ld\n" + +#: readelf.c:9680 +#, c-format +msgid " Version: %d\n" +msgstr " Phiên bản: %d\n" + +#: readelf.c:9681 +#, c-format +msgid " Offset into .debug_info: %lx\n" +msgstr " Hiệu số vào « .debug_info » (thông tin gỡ lỗi): %lx\n" + +#: readelf.c:9682 +#, c-format +msgid " Pointer Size: %d\n" +msgstr " Kích cỡ con trỏ : %d\n" + +#: readelf.c:9683 +#, c-format +msgid " Segment Size: %d\n" +msgstr " Kích cỡ phân đoạn: %d\n" + +#: readelf.c:9685 +#, c-format +msgid "" +"\n" +" Address Length\n" +msgstr "" +"\n" +" Độ dài địa chỉ\n" + +#: readelf.c:9741 +#, c-format +msgid "" +"\n" +"The .debug_ranges section is empty.\n" +msgstr "" +"\n" +"Phần « .debug_ranges » (các phạm vị gỡ lỗi) rỗng.\n" + +#. FIXME: Should we handle this case? +#: readelf.c:9786 +msgid "Range lists in .debug_info section aren't in ascending order!\n" +msgstr "" +"• Các danh sách phạm vị trong phần « .debug_info » (thông tin gỡ lỗi) không " +"phải theo thứ tự dần. •\n" + +#: readelf.c:9789 +msgid "No range lists in .debug_info section!\n" +msgstr "" +"• Không có danh sách phạm vị trong phần « .debug_info » (thông tin gỡ lỗi). " +"•\n" + +#: readelf.c:9792 +#, c-format +msgid "Range lists in .debug_ranges section start at 0x%lx\n" +msgstr "" +"Danh sách phạm vị trong phần « .debug_ranges » (các phạm vị gỡ lỗi) bắt đầu " +"tại 0x%lx\n" + +#: readelf.c:9795 +#, c-format +msgid "" +"Contents of the .debug_ranges section:\n" +"\n" +msgstr "" +"Nội dung của phần « .debug_ranges » (các phạm vị gỡ lỗi):\n" +"\n" + +#: readelf.c:9796 +#, c-format +msgid " Offset Begin End\n" +msgstr " HIệu Đầu Cuối\n" + +#: readelf.c:9820 +#, c-format +msgid "There is a hole [0x%lx - 0x%lx] in .debug_ranges section.\n" +msgstr "" +"Có một lỗ [0x%lx - 0x%lx] trong phần « .debug_ranges » (các phạm vị gỡ " +"lỗi).\n" + +#: readelf.c:9823 +#, c-format +msgid "There is an overlap [0x%lx - 0x%lx] in .debug_ranges section.\n" +msgstr "" +"Có một chồng lấp [0x%lx - 0x%lx] trong phần « .debug_ranges » (các phạm vị " +"gỡ lỗi).\n" + +#: readelf.c:10017 +#, c-format +msgid "The section %s contains:\n" +msgstr "Phần %s chứa:\n" + +#: readelf.c:10663 +#, c-format +msgid "unsupported or unknown DW_CFA_%d\n" +msgstr "« DW_CFA_%d » không được hỗ trợ, hay không rõ\n" + +#: readelf.c:10688 +#, c-format +msgid "Displaying the debug contents of section %s is not yet supported.\n" +msgstr "Chưa hỗ trợ khả năng hiển thị nội dung phần %s.\n" + +#: readelf.c:10732 +#, c-format +msgid "" +"\n" +"Section '%s' has no debugging data.\n" +msgstr "" +"\n" +"Phần « %s » không có dữ liệu gỡ lỗi nào.\n" + +#: readelf.c:10746 +msgid "debug section data" +msgstr "dữ liệu phần gỡ lỗi" + +#: readelf.c:10765 +#, c-format +msgid "Unrecognized debug section: %s\n" +msgstr "Không nhận diện phần gỡ lỗi: %s\n" + +#: readelf.c:10800 +#, c-format +msgid "Section %d was not dumped because it does not exist!\n" +msgstr "• Phần %d không được đổ vì nó không tồn tại. •\n" + +#: readelf.c:10872 readelf.c:11236 +msgid "liblist" +msgstr "danh sách thư viên" + +#: readelf.c:10961 +msgid "options" +msgstr "tùy chọn" + +#: readelf.c:10991 +#, c-format +msgid "" +"\n" +"Section '%s' contains %d entries:\n" +msgstr "" +"\n" +"Phần « %s » chứa %d mục nhập:\n" + +#: readelf.c:11152 +msgid "conflict list found without a dynamic symbol table" +msgstr "tìm danh sách xung đột không có bảng ký hiệu động" + +#: readelf.c:11168 readelf.c:11182 +msgid "conflict" +msgstr "xung đột" + +#: readelf.c:11192 +#, c-format +msgid "" +"\n" +"Section '.conflict' contains %lu entries:\n" +msgstr "" +"\n" +"Phần « .conflict » (xung đột) chứa %lu mục nhập:\n" + +#: readelf.c:11194 +msgid " Num: Index Value Name" +msgstr " Số : CMục Giá trị Tên" + +#: readelf.c:11243 +msgid "liblist string table" +msgstr "bảng chuỗi danh sách thư viên" + +#: readelf.c:11252 +#, c-format +msgid "" +"\n" +"Library list section '%s' contains %lu entries:\n" +msgstr "" +"\n" +"Phần danh sách thư viên « %s » chứa %lu mục nhập:\n" + +#: readelf.c:11303 +msgid "NT_AUXV (auxiliary vector)" +msgstr "NT_AUXV (véc-tơ phụ)" + +#: readelf.c:11305 +msgid "NT_PRSTATUS (prstatus structure)" +msgstr "NT_PRSTATUS (cấu trúc trạng thái prstatus)" + +#: readelf.c:11307 +msgid "NT_FPREGSET (floating point registers)" +msgstr "NT_FPREGSET (thanh ghi điểm phù động)" + +#: readelf.c:11309 +msgid "NT_PRPSINFO (prpsinfo structure)" +msgstr "NT_PRPSINFO (cấu trúc thông tin prpsinfo)" + +#: readelf.c:11311 +msgid "NT_TASKSTRUCT (task structure)" +msgstr "NT_TASKSTRUCT (cấu trúc tác vụ)" + +#: readelf.c:11313 +msgid "NT_PRXFPREG (user_xfpregs structure)" +msgstr "NT_PRXFPREG (cấu trúc « user_xfpregs »)" + +#: readelf.c:11315 +msgid "NT_PSTATUS (pstatus structure)" +msgstr "NT_PSTATUS (cấu trúc trạng thái pstatus)" + +#: readelf.c:11317 +msgid "NT_FPREGS (floating point registers)" +msgstr "NT_FPREGS (thanh ghi điểm phù động)" + +#: readelf.c:11319 +msgid "NT_PSINFO (psinfo structure)" +msgstr "NT_PSINFO (cấu trúc thông tin psinfo)" + +#: readelf.c:11321 +msgid "NT_LWPSTATUS (lwpstatus_t structure)" +msgstr "NT_LWPSTATUS (cấu trúc trạng thái « lwpstatus_t »)" + +#: readelf.c:11323 +msgid "NT_LWPSINFO (lwpsinfo_t structure)" +msgstr "NT_LWPSINFO (cấu trúc thông tin « lwpsinfo_t »)" + +#: readelf.c:11325 +msgid "NT_WIN32PSTATUS (win32_pstatus structure)" +msgstr "NT_WIN32PSTATUS (cấu trúc trạng thái « win32_pstatus »)" + +#: readelf.c:11333 +msgid "NT_VERSION (version)" +msgstr "NT_VERSION (phiên bản)" + +#: readelf.c:11335 +msgid "NT_ARCH (architecture)" +msgstr "NT_ARCH (architecture)" + +#: readelf.c:11340 readelf.c:11362 +#, c-format +msgid "Unknown note type: (0x%08x)" +msgstr "Không biết kiểu ghi chú : (0x%08x)" + +#. NetBSD core "procinfo" structure. +#: readelf.c:11352 +msgid "NetBSD procinfo structure" +msgstr "Cấu trúc thông tin tiến trình procinfo NetBSD" + +#: readelf.c:11379 readelf.c:11393 +msgid "PT_GETREGS (reg structure)" +msgstr "PT_GETREGS (cấu trúc thanh ghi)" + +#: readelf.c:11381 readelf.c:11395 +msgid "PT_GETFPREGS (fpreg structure)" +msgstr "PT_GETFPREGS (cấu trúc thanh ghi « fpreg »)" + +# Name: don't translate / Tên: đừng dịch +#: readelf.c:11401 +#, c-format +msgid "PT_FIRSTMACH+%d" +msgstr "PT_FIRSTMACH+%d" + +#: readelf.c:11447 ui/bookmarks.glade.h:51 +msgid "notes" +msgstr "ghi chú" + +#: readelf.c:11453 +#, c-format +msgid "" +"\n" +"Notes at offset 0x%08lx with length 0x%08lx:\n" +msgstr "" +"\n" +"Gặp ghi chú tại hiệu số 0x%08lx có độ dài 0x%08lx:\n" + +#: readelf.c:11455 +#, c-format +msgid " Owner\t\tData size\tDescription\n" +msgstr " Chủ\t\tCỡ dữ liệu\tMô tả\n" + +#: readelf.c:11474 +#, c-format +msgid "corrupt note found at offset %x into core notes\n" +msgstr "tìm ghi chú bị hỏng tại hiệu số %x vào ghi chú lõi\n" + +#: readelf.c:11476 +#, c-format +msgid " type: %x, namesize: %08lx, descsize: %08lx\n" +msgstr " kiểu: %x, cỡ_tên: %08lx, cỡ_mô_tả: %08lx\n" + +#: readelf.c:11574 +#, c-format +msgid "No note segments present in the core file.\n" +msgstr "Không có phân đoạn ghi chú trong tập tin lõi.\n" + +#: readelf.c:11653 +msgid "" +"This instance of readelf has been built without support for a\n" +"64 bit data type and so it cannot read 64 bit ELF files.\n" +msgstr "" +"Tức thời readelf này đã được xây dụng\n" +"không có hỗ trợ kiểu dữ liệu 64-bit\n" +"nên không thể đọc tập tin ELF kiểu 64-bit.\n" + +#: readelf.c:11700 readelf.c:12059 +#, c-format +msgid "%s: Failed to read file header\n" +msgstr "%s: việc đọc dòng đầu tập tin bị lỗi\n" + +#: readelf.c:11713 +#, c-format +msgid "" +"\n" +"File: %s\n" +msgstr "" +"\n" +"Tập tin: %s\n" + +#: readelf.c:11876 readelf.c:11897 readelf.c:11934 readelf.c:12014 +#, c-format +msgid "%s: failed to read archive header\n" +msgstr "%s: việc đọc dòng đầu kho bị lỗi\n" + +#: readelf.c:11887 +#, c-format +msgid "%s: failed to skip archive symbol table\n" +msgstr "%s: việc nhảy qua bảng ký hiệu kho bị lỗi\n" + +#: readelf.c:11919 +#, c-format +msgid "%s: failed to read string table\n" +msgstr "%s: việc đọc bảng chuỗi bị lỗi\n" + +#: readelf.c:11955 +#, c-format +msgid "%s: invalid archive string table offset %lu\n" +msgstr "%s: hiệu số bảng chuỗi kho không hợp lệ %lu\n" + +#: readelf.c:11971 +#, c-format +msgid "%s: bad archive file name\n" +msgstr "%s: tên tập tin kho sai\n" + +#: readelf.c:12003 +#, c-format +msgid "%s: failed to seek to next archive header\n" +msgstr "%s: việc tìm tới dòng đầu kho kế tiếp bị lỗi\n" + +#: readelf.c:12037 +#, c-format +msgid "'%s': No such file\n" +msgstr "« %s »: không có tập tin như vậy\n" + +#: readelf.c:12039 +#, c-format +msgid "Could not locate '%s'. System error message: %s\n" +msgstr "Không thể định vị « %s ». Thông điệp lỗi hệ thống: %s\n" + +#: readelf.c:12046 +#, c-format +msgid "'%s' is not an ordinary file\n" +msgstr "« %s » không phải là tập tin chuẩn\n" + +#: readelf.c:12053 +#, c-format +msgid "Input file '%s' is not readable.\n" +msgstr "Tập tin nhập « %s » không có khả năng đọc.\n" + +#: rename.c:127 +#, c-format +msgid "%s: cannot set time: %s" +msgstr "%s: không thể lập thời gian: %s" + +#. We have to clean up here. +#: rename.c:162 rename.c:200 +#, c-format +msgid "unable to rename '%s' reason: %s" +msgstr "không thể đổi tên %s vì lý do : %s" + +#: rename.c:208 +#, c-format +msgid "unable to copy file '%s' reason: %s" +msgstr "không thể sao chép tập tin « %s » vì lý do : %s" + +#: resbin.c:132 +#, c-format +msgid "%s: not enough binary data" +msgstr "%s: không đủ dữ liệu nhị phân" + +#: resbin.c:148 +msgid "null terminated unicode string" +msgstr "chuỗi Unicode không được chấm dứt rỗng" + +#: resbin.c:175 resbin.c:181 +msgid "resource ID" +msgstr "ID tài nguyên" + +#: resbin.c:221 +msgid "cursor" +msgstr "con chạy" + +#: resbin.c:253 resbin.c:260 +msgid "menu header" +msgstr "dòng đầu trình đơn" + +#: resbin.c:270 +msgid "menuex header" +msgstr "dòng đầu trình đơn menuex" + +#: resbin.c:274 +msgid "menuex offset" +msgstr "hiệu số trình đơn menuex" + +#: resbin.c:281 +#, c-format +msgid "unsupported menu version %d" +msgstr "phiên bản trình đơn không được hỗ trợ %d" + +#: resbin.c:306 resbin.c:321 resbin.c:384 +msgid "menuitem header" +msgstr "dòng đầu mục trình đơn" + +#: resbin.c:414 +msgid "menuitem" +msgstr "mục trình đơn" + +#: resbin.c:453 resbin.c:481 +msgid "dialog header" +msgstr "dòng đầu đối thoại" + +#: resbin.c:471 +#, c-format +msgid "unexpected DIALOGEX version %d" +msgstr "ngờ đối thoại DIALOGEX phiên bản %d" + +#: resbin.c:516 +msgid "dialog font point size" +msgstr "kích cỡ điểm phông chữ đối thoại" + +#: resbin.c:524 +msgid "dialogex font information" +msgstr "thông tin phông chữ đối thoại dialogex" + +#: resbin.c:550 resbin.c:568 +msgid "dialog control" +msgstr "điều kiện đối thoại" + +#: resbin.c:560 +msgid "dialogex control" +msgstr "điều kiện đối thoại dialogex" + +#: resbin.c:589 +msgid "dialog control end" +msgstr "kết thúc điều khiển đối thoại" + +#: resbin.c:601 +msgid "dialog control data" +msgstr "dữ liệu điều khiển đối thoại" + +#: resbin.c:642 +msgid "stringtable string length" +msgstr "độ dài bảng chuỗi" + +#: resbin.c:652 +msgid "stringtable string" +msgstr "chuỗi bảng chuỗi" + +#: resbin.c:683 +msgid "fontdir header" +msgstr "dòng đầu thư mục phông chữ" + +#: resbin.c:696 +msgid "fontdir" +msgstr "thư mục phông chữ" + +#: resbin.c:712 +msgid "fontdir device name" +msgstr "tên thiết bị thư mục phông chữ" + +#: resbin.c:718 +msgid "fontdir face name" +msgstr "tên mặt thư mục phông chữ" + +#: resbin.c:759 ../srcore/default.xml.in.h:21 ../srcore/verbose.xml.in.h:21 +#: ../src/orca/rolenames.py:149 +msgid "accelerator" +msgstr "phím tắt" + +#: resbin.c:819 +msgid "group cursor header" +msgstr "dòng đầu con chạy nhóm" + +#: resbin.c:823 +#, c-format +msgid "unexpected group cursor type %d" +msgstr "kiểu con chạy nhóm bất ngờ %d" + +#: resbin.c:838 +msgid "group cursor" +msgstr "con chạy nhóm" + +#: resbin.c:875 +msgid "group icon header" +msgstr "dòng đầu biểu tượng nhóm" + +#: resbin.c:879 +#, c-format +msgid "unexpected group icon type %d" +msgstr "kiểu biểu tượng nhóm bất ngờ %d" + +#: resbin.c:894 +msgid "group icon" +msgstr "biểu tượng nhóm" + +#: resbin.c:957 resbin.c:1174 +msgid "unexpected version string" +msgstr "chuỗi phiên bản bất ngờ" + +#: resbin.c:989 +#, c-format +msgid "version length %d does not match resource length %lu" +msgstr "độ dài phiên bản %d không khớp độ dài tài nguyên %lu." + +#: resbin.c:993 +#, c-format +msgid "unexpected version type %d" +msgstr "kiểu phiên bản bất ngờ %d" + +#: resbin.c:1005 +#, c-format +msgid "unexpected fixed version information length %d" +msgstr "độ dài thông tin phiên bản cố định bất ngờ %d" + +#: resbin.c:1008 +msgid "fixed version info" +msgstr "thông tin phiên bản cố định" + +#: resbin.c:1012 +#, c-format +msgid "unexpected fixed version signature %lu" +msgstr "chữ ký phiên bản cố định bất ngờ %lu" + +#: resbin.c:1016 +#, c-format +msgid "unexpected fixed version info version %lu" +msgstr "phiên bản thông tin phiên bản cố định %lu" + +#: resbin.c:1045 +msgid "version var info" +msgstr "hông tin tạm phiên bản" + +#: resbin.c:1062 +#, c-format +msgid "unexpected stringfileinfo value length %d" +msgstr "độ dài giá trị thông tin tập tin chuỗi bất ngờ %d" + +#: resbin.c:1072 +#, c-format +msgid "unexpected version stringtable value length %d" +msgstr "độ dài giá trị bảng chuỗi phiên bản bất ngờ %d" + +#: resbin.c:1106 +#, c-format +msgid "unexpected version string length %d != %d + %d" +msgstr "độ dài chuỗi phiên bản bất ngờ %d != %d + %d" + +#: resbin.c:1117 +#, c-format +msgid "unexpected version string length %d < %d" +msgstr "độ dài chuỗi phiên bản bất ngờ %d < %d" + +#: resbin.c:1134 +#, c-format +msgid "unexpected varfileinfo value length %d" +msgstr "độ dài giá trị thông tin tập tin tạm bất ngờ %d" + +#: resbin.c:1153 +msgid "version varfileinfo" +msgstr "thông tin tập tin tạm phiên bản" + +#: resbin.c:1168 +#, c-format +msgid "unexpected version value length %d" +msgstr "nđộ dài giá trị phiên bản bất ngờ %d" + +#: rescoff.c:126 +msgid "filename required for COFF input" +msgstr "tên tập tin cần thiết cho dữ liệu nhập COFF" + +#: rescoff.c:143 +#, c-format +msgid "%s: no resource section" +msgstr "%s: không có phần tài nguyên" + +#: rescoff.c:150 +msgid "can't read resource section" +msgstr "không thể đọc phần tài nguyên" + +#: rescoff.c:174 +#, c-format +msgid "%s: %s: address out of bounds" +msgstr "%s: %s: địa chỉ ở ngoại phạm vị" + +#: rescoff.c:190 lib/file-type.c:46 +msgid "directory" +msgstr "thư mục" + +#: rescoff.c:218 +msgid "named directory entry" +msgstr "mục nhập thư mục có tên" + +#: rescoff.c:227 +msgid "directory entry name" +msgstr "tên mục nhập thư mục " + +#: rescoff.c:247 +msgid "named subdirectory" +msgstr "thư mục con có tên" + +#: rescoff.c:255 +msgid "named resource" +msgstr "tài nguyên có tên" + +#: rescoff.c:270 +msgid "ID directory entry" +msgstr "mục nhập thư mục ID" + +#: rescoff.c:287 +msgid "ID subdirectory" +msgstr "thư mục con ID" + +#: rescoff.c:295 +msgid "ID resource" +msgstr "tài nguyên ID" + +#: rescoff.c:318 +msgid "resource type unknown" +msgstr "không biết kiểu tài nguyên" + +#: rescoff.c:321 +msgid "data entry" +msgstr "mục nhập dữ liệu" + +#: rescoff.c:329 +msgid "resource data" +msgstr "dữ liệu tài nguyên" + +#: rescoff.c:334 +msgid "resource data size" +msgstr "kích cỡ dữ liệu tài nguyên" + +#: rescoff.c:427 +msgid "filename required for COFF output" +msgstr "tên tập tin cần thiết cho kết xuất COFF" + +#: rescoff.c:719 +msgid "can't get BFD_RELOC_RVA relocation type" +msgstr "không thể lấy kiểu việc định vị lại « BFD_RELOC_RVA »" + +#: resrc.c:238 resrc.c:309 +#, c-format +msgid "can't open temporary file `%s': %s" +msgstr "không thể mở tập tin tạm thời « %s »: %s" + +#: resrc.c:244 +#, c-format +msgid "can't redirect stdout: `%s': %s" +msgstr "không thể chuyển hướng thiết bị xuất chuẩn « %s »: %s" + +# Variable: don't translate / Biến: đừng dịch +#: resrc.c:260 +#, c-format +msgid "%s %s: %s" +msgstr "%s %s: %s" + +#: resrc.c:305 +#, c-format +msgid "can't execute `%s': %s" +msgstr "không thể thực hiện « %s »: %s" + +#: resrc.c:314 +#, c-format +msgid "Using temporary file `%s' to read preprocessor output\n" +msgstr "Đang dùng tập tin tạm thời « %s » để đọc dữ liệu xuất bộ tiền xử lý\n" + +#: resrc.c:321 +#, c-format +msgid "can't popen `%s': %s" +msgstr "Không thể popen (mở p) « %s »: %s" + +#: resrc.c:323 +#, c-format +msgid "Using popen to read preprocessor output\n" +msgstr "Đang dùng popen để đọc dữ liệu xuất bộ tiền xử lý\n" + +#: resrc.c:362 +#, c-format +msgid "Tried `%s'\n" +msgstr "Đã thử « %s »\n" + +#: resrc.c:373 +#, c-format +msgid "Using `%s'\n" +msgstr "Đang dùng « %s »\n" + +# Variable: don't translate / Biến: đừng dịch +#: resrc.c:529 +#, c-format +msgid "%s:%d: %s\n" +msgstr "%s:%d: %s\n" + +#: resrc.c:537 +#, c-format +msgid "%s: unexpected EOF" +msgstr "%s: gặp kết thúc tập tin bất ngờ" + +#: resrc.c:586 +#, c-format +msgid "%s: read of %lu returned %lu" +msgstr "%s: việc đọc %lu đã trả gởi %lu" + +#: resrc.c:624 resrc.c:1134 +#, c-format +msgid "stat failed on bitmap file `%s': %s" +msgstr "việc lấy các thông tin bị lỗi trên tập tin bitmap « %s »: %s" + +#: resrc.c:675 +#, c-format +msgid "cursor file `%s' does not contain cursor data" +msgstr "tập tin con chạy « %s » không chứa dữ liệu con chạy" + +#: resrc.c:707 resrc.c:1003 +#, c-format +msgid "%s: fseek to %lu failed: %s" +msgstr "%s: việc fseek (tìm f) tới %lu bị lỗi: %s" + +#: resrc.c:831 +msgid "help ID requires DIALOGEX" +msgstr "ID trợ giúp cần thiết DIALOGEX (đối thoại)" + +#: resrc.c:833 +msgid "control data requires DIALOGEX" +msgstr "dữ liệu điều khiển cần thiết DIALOGEX (đối thoại)" + +#: resrc.c:861 +#, c-format +msgid "stat failed on font file `%s': %s" +msgstr "việc lấy các thông tin bị lỗi trên tập tin phông chữ « %s »: %s" + +#: resrc.c:972 +#, c-format +msgid "icon file `%s' does not contain icon data" +msgstr "tập tin biểu tượng « %s » không chứa dữ liệu biểu tượng" + +#: resrc.c:1273 resrc.c:1308 +#, c-format +msgid "stat failed on file `%s': %s" +msgstr "việc lấy các thông tin bị lỗi trên tập tin « %s »: %s" + +#: resrc.c:1494 +#, c-format +msgid "can't open `%s' for output: %s" +msgstr "không thể mở « %s » để xuất: %s" + +#: size.c:81 +#, c-format +msgid " Displays the sizes of sections inside binary files\n" +msgstr " Hiển thị kích cỡ của các phần ở trong tập tin nhị phân\n" + +#: size.c:82 +#, c-format +msgid " If no input file(s) are specified, a.out is assumed\n" +msgstr "Nếu chưa ghi rõ tập tin nhập, giả sử \n" + +#: size.c:83 +#, c-format +msgid "" +" The options are:\n" +" -A|-B --format={sysv|berkeley} Select output style (default is %s)\n" +" -o|-d|-x --radix={8|10|16} Display numbers in octal, decimal or " +"hex\n" +" -t --totals Display the total sizes (Berkeley " +"only)\n" +" --target= Set the binary file format\n" +" -h --help Display this information\n" +" -v --version Display the program's version\n" +"\n" +msgstr "" +" Tùy chọn:\n" +" -A|-B --format={sysv|berkeley}\n" +"\t\t\tChọn kiểu dáng xuất (mặc định là %s)\n" +"\t\t\t(dạng thức)\n" +" -o|-d|-x --radix={8|10|16}\n" +"\t\t\tHiển thị số dạng bát phân, thập phân hay thập lục\n" +"\t\t\t(cơ sở)\n" +" -t --totals Hiển thị các kích cỡ _tổng cổng_ (chỉ " +"Berkeley)\n" +" --target= \tLập dạng thức tập tin nhị phân\n" +"\t\t\t(đích)\n" +" -h --help Hiển thị _trợ giúp_ này\n" +" -v --version Hiển thị _phiên bản_ của chương trình này\n" +"\n" + +#: size.c:153 +#, c-format +msgid "invalid argument to --format: %s" +msgstr "đối sô không hợp lệ tới « --format » (dạng thức): %s" + +#: size.c:180 +#, c-format +msgid "Invalid radix: %s\n" +msgstr "Cơ sở không hợp lệ: %s\n" + +#: srconv.c:1722 +#, c-format +msgid "Convert a COFF object file into a SYSROFF object file\n" +msgstr "" +"Chuyển đổi một tập tin đối tượng COFF thành một tập tin đối tượng SYSROFF\n" + +#: srconv.c:1723 +#, c-format +msgid "" +" The options are:\n" +" -q --quick (Obsolete - ignored)\n" +" -n --noprescan Do not perform a scan to convert commons into defs\n" +" -d --debug Display information about what is being done\n" +" -h --help Display this information\n" +" -v --version Print the program's version number\n" +msgstr "" +" Tùy chọn:\n" +" -q --quick \t(Cũ nên bị bỏ qua)\n" +" -n --noprescan\n" +"\t\tĐừng quét để chuyển đổi các điều dùng chung (common)\n" +"\t\tthành lời định nghĩa (def)\n" +"\t\t(không quét trước)\n" +" -d --debug \t\t\tHiển thị thông tin về hành động hiện thời\n" +"\t\t(gỡ lỗi)\n" +" -h --help \t\t\tHiển thị _trợ giúp_ này\n" +" -v --version \t\tIn ra số thứ tự _phiên bản_ của chương trình\n" + +#: srconv.c:1866 +#, c-format +msgid "unable to open output file %s" +msgstr "không thể mở tập tin kết xuất %s" + +#: stabs.c:330 stabs.c:1708 +msgid "numeric overflow" +msgstr "tràn thuộc số" + +#: stabs.c:340 +#, c-format +msgid "Bad stab: %s\n" +msgstr "stab sai: %s\n" + +#: stabs.c:348 +#, c-format +msgid "Warning: %s: %s\n" +msgstr "Cảnh báo : %s: %s\n" + +#: stabs.c:458 +#, c-format +msgid "N_LBRAC not within function\n" +msgstr "« N_LBRAC » không phải ở trong hàm\n" + +#: stabs.c:497 +#, c-format +msgid "Too many N_RBRACs\n" +msgstr "Quá nhiều « N_RBRAC »\n" + +#: stabs.c:738 +msgid "unknown C++ encoded name" +msgstr "không biết tên mã C++" + +#. Complain and keep going, so compilers can invent new +#. cross-reference types. +#: stabs.c:1253 +msgid "unrecognized cross reference type" +msgstr "không nhận diện kiểu tham chiếu chéo" + +#. Does this actually ever happen? Is that why we are worrying +#. about dealing with it rather than just calling error_type? +#: stabs.c:1800 +msgid "missing index type" +msgstr "thiếu kiểu chỉ mục" + +#: stabs.c:2114 +msgid "unknown virtual character for baseclass" +msgstr "không biết ký tự ảo cho hạng cơ bản" + +#: stabs.c:2132 +msgid "unknown visibility character for baseclass" +msgstr "không biết ký tự tính trạng hiển thị cho hạng cơ bản" + +#: stabs.c:2318 +msgid "unnamed $vb type" +msgstr "kiểu $vb chưa có tên" + +#: stabs.c:2324 +msgid "unrecognized C++ abbreviation" +msgstr "không nhận biết viết tắt C++" + +#: stabs.c:2400 +msgid "unknown visibility character for field" +msgstr "không biết ký tự tính trạng hiển thị cho trường" + +#: stabs.c:2652 +msgid "const/volatile indicator missing" +msgstr "thiếu chỉ thị bất biến/hay thay đổi" + +#: stabs.c:2888 +#, c-format +msgid "No mangling for \"%s\"\n" +msgstr "Không có việc tháo gỡ cho « %s »\n" + +#: stabs.c:3188 +msgid "Undefined N_EXCL" +msgstr "Chưa định nghĩa « N_EXCL »" + +#: stabs.c:3268 +#, c-format +msgid "Type file number %d out of range\n" +msgstr "Số kiểu tập tin %d ở ngoài phạm vi\n" + +#: stabs.c:3273 +#, c-format +msgid "Type index number %d out of range\n" +msgstr "Số kiểu chỉ mục %d ở ngoài phạm vi\n" + +#: stabs.c:3352 +#, c-format +msgid "Unrecognized XCOFF type %d\n" +msgstr "Không nhận diện kiểu XCOFF %d\n" + +#: stabs.c:3644 +#, c-format +msgid "bad mangled name `%s'\n" +msgstr "tên đã rối sai « %s »\n" + +#: stabs.c:3739 +#, c-format +msgid "no argument types in mangled string\n" +msgstr "không có kiểu đối số nào trong chuỗi đã rối\n" + +#: stabs.c:5093 +#, c-format +msgid "Demangled name is not a function\n" +msgstr "Tên đã tháo gỡ không phải là hàm\n" + +#: stabs.c:5135 +#, c-format +msgid "Unexpected type in v3 arglist demangling\n" +msgstr "Gặp kiểu bất ngờ trong việc tháo gỡ danh sách đối số v3\n" + +#: stabs.c:5202 +#, c-format +msgid "Unrecognized demangle component %d\n" +msgstr "Không nhận diện thành phần tháo gỡ %d\n" + +#: stabs.c:5254 +#, c-format +msgid "Failed to print demangled template\n" +msgstr "Việc in ra biểu mẫu đã tháo gỡ bị lỗi\n" + +#: stabs.c:5334 +#, c-format +msgid "Couldn't get demangled builtin type\n" +msgstr "Không thể lấy kiểu builtin (điều có sẵn) đã tháo gỡ\n" + +#: stabs.c:5383 +#, c-format +msgid "Unexpected demangled varargs\n" +msgstr "Gặp một số varargs (đối số biến) đã tháo gỡ bất ngờ\n" + +#: stabs.c:5390 +#, c-format +msgid "Unrecognized demangled builtin type\n" +msgstr "Không nhận diện kiểu builtin (điều có sẵn) đã tháo gỡ\n" + +#: strings.c:206 +#, c-format +msgid "invalid number %s" +msgstr "số không hợp lệ %s" + +#: strings.c:643 +#, c-format +msgid "invalid integer argument %s" +msgstr "đối số số nguyên không hợp lệ %s" + +#: strings.c:652 +#, c-format +msgid " Display printable strings in [file(s)] (stdin by default)\n" +msgstr "" +" Hiển thị các chuỗi có khả năng in trong [tập tin...] (mặc định là thiết bị " +"nhập chuẩn)\n" + +#: strings.c:653 +#, c-format +msgid "" +" The options are:\n" +" -a - --all Scan the entire file, not just the data section\n" +" -f --print-file-name Print the name of the file before each string\n" +" -n --bytes=[number] Locate & print any NUL-terminated sequence of " +"at\n" +" - least [number] characters (default 4).\n" +" -t --radix={o,d,x} Print the location of the string in base 8, 10 " +"or 16\n" +" -o An alias for --radix=o\n" +" -T --target= Specify the binary file format\n" +" -e --encoding={s,S,b,l,B,L} Select character size and endianness:\n" +" s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-" +"bit\n" +" -h --help Display this information\n" +" -v --version Print the program's version number\n" +msgstr "" +" Tùy chọn:\n" +" -a - --all \t\tQuét toàn bộ tập tin, không chỉ phần dữ " +"liệu\n" +"\t\t(hết)\n" +" -f --print-file-name \t\t\t _In ra tên tập tin_ trước mỗi chuỗi\n" +" -n --bytes=[số]\n" +"\t\tĐịnh vị và in ra dãy đã chấm dứt RỖNG nào tại số byte này\n" +" - số ký tự tối thiểu (mặc định là 4).\n" +" -t --radix={o,d,x}\n" +"\t\tIn ra địa điểm của chuỗi dạng bát phân, thập phân hay thập lục\n" +"\t\t(cơ sở)\n" +" -o \t\tBiệt hiệu cho « --radix=o » \n" +" -T --target= \t\tGhi rõ dạng thức tập tin nhị phân\n" +"\t\t(đích)\n" +" -e --encoding={s,S,b,l,B,L}\n" +"\t\tChọn kích cỡ ký tự và tính trạng cuối (endian):\n" +" \ts = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit\n" +" -h --help \t\tHiển thị _trợ giúp_ này\n" +" -v --version \t\tIn ra số thứ tự _phiên bản_ của chương " +"trình\n" + +#: sysdump.c:649 +#, c-format +msgid "Print a human readable interpretation of a SYSROFF object file\n" +msgstr "In ra lời giải dịch tập tin đối tượng SYSROFF cho người đọc\n" + +#: sysdump.c:650 +#, c-format +msgid "" +" The options are:\n" +" -h --help Display this information\n" +" -v --version Print the program's version number\n" +msgstr "" +" Tùy chọn:\n" +" -h --help \t\tHiển thị _trợ giúp_ này\n" +" -v --version \t\tIn ra số thứ tự _phiên bản_ của chương " +"trình\n" + +#: sysdump.c:715 +#, c-format +msgid "cannot open input file %s" +msgstr "không thể mở tập tin nhập %s" + +#: version.c:35 ldver.c:42 +#, c-format +msgid "Copyright 2005 Free Software Foundation, Inc.\n" +msgstr "Bản quyền © năm 2005 Tổ chức Phần mềm Tự do.\n" + +#: version.c:36 ldver.c:43 +#, c-format +msgid "" +"This program is free software; you may redistribute it under the terms of\n" +"the GNU General Public License. This program has absolutely no warranty.\n" +msgstr "" +"Chương trình này là phần mềm tự do; bạn có thể phát hành lại\n" +"nó với điều kiện của Quyền Công Chung GNU (GPL).\n" +"Chương trình này không bảo đảm gì cả.\n" + +#: windres.c:204 +#, c-format +msgid "can't open %s `%s': %s" +msgstr "Không thể mở %s « %s »: %s" + +#: windres.c:370 +#, c-format +msgid ": expected to be a directory\n" +msgstr ": ngờ là thư mục\n" + +#: windres.c:382 +#, c-format +msgid ": expected to be a leaf\n" +msgstr ": ngờ là lá\n" + +#: windres.c:391 src/po-charset.c:324 src/po-charset.c:351 +#: util/install-info.c:154 +#, c-format +msgid "%s: warning: " +msgstr "%s: cảnh báo :" + +#: windres.c:393 +#, c-format +msgid ": duplicate value\n" +msgstr ": giá trị trùng\n" + +#: windres.c:543 +#, c-format +msgid "unknown format type `%s'" +msgstr "không biết kiểu dạng thức « %s »" + +#: windres.c:544 +#, c-format +msgid "%s: supported formats:" +msgstr "%s: dạng thức hỗ trợ :" + +#. Otherwise, we give up. +#: windres.c:627 +#, c-format +msgid "can not determine type of file `%s'; use the -J option" +msgstr "không thể quyết định kiểu tập tin « %s »: hãy sử dụng tùy chọn « -J »" + +#: windres.c:639 +#, c-format +msgid "Usage: %s [option(s)] [input-file] [output-file]\n" +msgstr "Usage: %s [tùy_chọn...] [tập_tin_nhập] [tập_tin_xuất]\n" + +#: windres.c:641 +#, c-format +msgid "" +" The options are:\n" +" -i --input= Name input file\n" +" -o --output= Name output file\n" +" -J --input-format= Specify input format\n" +" -O --output-format= Specify output format\n" +" -F --target= Specify COFF target\n" +" --preprocessor= Program to use to preprocess rc file\n" +" -I --include-dir= Include directory when preprocessing rc file\n" +" -D --define [=] Define SYM when preprocessing rc file\n" +" -U --undefine Undefine SYM when preprocessing rc file\n" +" -v --verbose Verbose - tells you what it's doing\n" +" -l --language= Set language when reading rc file\n" +" --use-temp-file Use a temporary file instead of popen to " +"read\n" +" the preprocessor output\n" +" --no-use-temp-file Use popen (default)\n" +msgstr "" +" Tùy chọn:\n" +" -i --input= \t\t Lập tập tin _nhập_\n" +" -o --output= \t\t Lập tập tin _xuất_\n" +" -J --input-format= \t Ghi rõ _dạng thức " +"nhập_\n" +" -O --output-format= \t Ghi rõ _dạng thức " +"xuất_\n" +" -F --target=<đích> \t\t\t Ghi rõ _đích_ COFF\n" +" --preprocessor=\n" +"\t\tChương trình cần dùng để tiền xử lý tập tin rc (tài nguyên)\n" +"\t\t(bộ tiền xử lý)\n" +" -I --include-dir=\n" +"\t\t_Gồm thư mục_ khi tiền xử lý tập tin rc (tài nguyên)\n" +" -D --define [=]\n" +"\t\t_Định nghĩa_ ký hiệu khi tiền xử lý tập tin rc (tài nguyên)\n" +" -U --undefine \n" +"\t\t_Hủy định nghĩa_ ký hiệu khi tiền xử lý tập tin rc (tài nguyên)\n" +" -v --verbose _Chi tiết_: xuất thông tin về hành động hiện " +"thời\n" +" -l --language= Lập _ngôn ngữ_ để đọc tập tin rc (tài nguyên)\n" +" --use-temp-file\n" +"\t\t_Dùng tập tin tạm thời_ thay vào popen để đọc kết xuất tiền xử lý\n" +" --no-use-temp-file \t\t\t Dùng popen (mặc định)\n" +"\t\t(không dùng tập tin tạm thời)\n" + +#: windres.c:657 +#, c-format +msgid " --yydebug Turn on parser debugging\n" +msgstr " --yydebug Bật khả năng gỡ lỗi kiểu bộ phân tách\n" + +#: windres.c:660 +#, c-format +msgid "" +" -r Ignored for compatibility with rc\n" +" -h --help Print this help message\n" +" -V --version Print version information\n" +msgstr "" +" -r\t\t\t\t\t \t \t\t Bị bỏ qua để tương thích với rc (tài nguyên)\n" +" -h, --help \t\t\t\t rctrợ giúp_ này\n" +" -V, --version \t\t\t\t In ra thông tin _phiên bản_\n" + +#: windres.c:664 +#, c-format +msgid "" +"FORMAT is one of rc, res, or coff, and is deduced from the file name\n" +"extension if not specified. A single file name is an input file.\n" +"No input-file is stdin, default rc. No output-file is stdout, default rc.\n" +msgstr "" +"DẠNG THỨC là một của rc, res hay coff, và được quyết định\n" +"từ phần mở rộng tên tập tin nếu chưa ghi rõ.\n" +"Một tên tập tin đơn là tập tin nhập. Không có tập tin nhập thì\n" +"thiết bị nhập chuẩn, mặc định là rc. Không có tập tin xuất thì\n" +"thiết bị xuất chuẩn, mặc định là rc.\n" + +#: windres.c:800 +msgid "invalid option -f\n" +msgstr "tùy chọn không hợp lệ « -f »\n" + +#: windres.c:805 +msgid "No filename following the -fo option.\n" +msgstr "Không có tên tập tin đi sau tùy chọn « -fo ».\n" + +#: windres.c:863 +#, c-format +msgid "" +"Option -I is deprecated for setting the input format, please use -J " +"instead.\n" +msgstr "" +"Tùy chọn « -l » bị phản đối để lập dạng thức nhập, hãy dùng « -J » thay " +"thế.\n" + +#: windres.c:981 +msgid "no resources" +msgstr "không có tài nguyên nào" + +#: wrstabs.c:354 wrstabs.c:1915 +#, c-format +msgid "string_hash_lookup failed: %s" +msgstr "việc « string_hash_lookup » (tra tìm băm chuỗi) bị lỗi: %s" + +#: wrstabs.c:635 +#, c-format +msgid "stab_int_type: bad size %u" +msgstr "stab_int_type: (kiểu số nguyên stab) kích cỡ sai %u" + +#: wrstabs.c:1393 +#, c-format +msgid "%s: warning: unknown size for field `%s' in struct" +msgstr "%s: cảnh báo : không biết kích cỡ cho trường « %s » trong cấu trúc" + +#: cmdline/apt-cache.cc:135 +#, c-format +msgid "Package %s version %s has an unmet dep:\n" +msgstr "Gói %s phiên bản %s phụ thuộc vào phần mềm chưa có :\n" + +#: cmdline/apt-cache.cc:1508 +#, c-format +msgid "Unable to locate package %s" +msgstr "Không thể định vị gói %s" + +#: cmdline/apt-cache.cc:232 +msgid "Total package names : " +msgstr "Tổng tên gói: " + +#: cmdline/apt-cache.cc:272 +msgid " Normal packages: " +msgstr " Gói bình thường: " + +#: cmdline/apt-cache.cc:273 +msgid " Pure virtual packages: " +msgstr " Gói ảo nguyên chất: " + +#: cmdline/apt-cache.cc:274 +msgid " Single virtual packages: " +msgstr " Gói ảo đơn: " + +#: cmdline/apt-cache.cc:275 +msgid " Mixed virtual packages: " +msgstr " Gói ảo đã pha trộn: " + +#: cmdline/apt-cache.cc:276 +msgid " Missing: " +msgstr " Thiếu : " + +#: cmdline/apt-cache.cc:278 +msgid "Total distinct versions: " +msgstr "Tổng phiên bản riêng: " + +#: cmdline/apt-cache.cc:280 +msgid "Total dependencies: " +msgstr "Tổng cách phụ thuộc: " + +#: cmdline/apt-cache.cc:283 +msgid "Total ver/file relations: " +msgstr "Tổng cách liên quan phiên bản và tập tin: " + +#: cmdline/apt-cache.cc:285 +msgid "Total Provides mappings: " +msgstr "Tổng cách ảnh xạ Miễn là: " + +#: cmdline/apt-cache.cc:297 +msgid "Total globbed strings: " +msgstr "Tổng chuỗi mở rộng mẫu tìm kiếm: " + +#: cmdline/apt-cache.cc:311 +msgid "Total dependency version space: " +msgstr "Tổng chỗ cho cách phụ thuộc vào phiên bản: " + +#: cmdline/apt-cache.cc:316 +msgid "Total slack space: " +msgstr "Tổng chỗ chưa dùng: " + +#: cmdline/apt-cache.cc:324 +msgid "Total space accounted for: " +msgstr "Tổng chỗ sẽ dùng: " + +#: cmdline/apt-cache.cc:446 cmdline/apt-cache.cc:1189 +#, c-format +msgid "Package file %s is out of sync." +msgstr "Tập tin gói %s không đồng bộ được." + +#: cmdline/apt-cache.cc:1231 +msgid "You must give exactly one pattern" +msgstr "Bạn phải đưa ra đúng một mẫu" + +#: cmdline/apt-cache.cc:1385 +msgid "No packages found" +msgstr "Không tìm thấy gói" + +#: cmdline/apt-cache.cc:1462 +msgid "Package files:" +msgstr "Tập tin gói:" + +#: cmdline/apt-cache.cc:1469 cmdline/apt-cache.cc:1555 +msgid "Cache is out of sync, can't x-ref a package file" +msgstr "" +"Bộ nhớ tạm không đồng bộ được nên không thể tham chiếu chéo tập tin gói" + +# Variable: do not translate/ biến: đừng dịch +#: cmdline/apt-cache.cc:1470 +#, c-format +msgid "%4i %s\n" +msgstr "%4i %s\n" + +#. Show any packages have explicit pins +#: cmdline/apt-cache.cc:1482 +msgid "Pinned packages:" +msgstr "Các gói đã ghim:" + +#: cmdline/apt-cache.cc:1494 cmdline/apt-cache.cc:1535 +msgid "(not found)" +msgstr "(không tìm thấy)" + +#. Installed version +#: cmdline/apt-cache.cc:1515 +msgid " Installed: " +msgstr " Đã cài đặt: " + +#: oggenc/oggenc.c:526 oggenc/oggenc.c:531 +msgid "(none)" +msgstr "(không có)" + +#. Candidate Version +#: cmdline/apt-cache.cc:1522 +msgid " Candidate: " +msgstr " Ứng cử : " + +#: cmdline/apt-cache.cc:1532 +msgid " Package pin: " +msgstr " Ghim gói: " + +#. Show the priority tables +#: cmdline/apt-cache.cc:1541 +msgid " Version table:" +msgstr " Bảng phiên bản:" + +# Variable: do not translate/ biến: đừng dịch +#: cmdline/apt-cache.cc:1556 +#, c-format +msgid " %4i %s\n" +msgstr " %4i %s\n" + +#: ftparchive/apt-ftparchive.cc:545 cmdline/apt-get.cc:2260 +#, c-format +msgid "%s %s for %s %s compiled on %s %s\n" +msgstr "%s %s cho %s %s được biên dịch vào %s %s\n" + +#: cmdline/apt-cache.cc:1658 cmdline/apt-cache.cc:1653 +msgid "" +"Usage: apt-cache [options] command\n" +" apt-cache [options] add file1 [file2 ...]\n" +" apt-cache [options] showpkg pkg1 [pkg2 ...]\n" +" apt-cache [options] showsrc pkg1 [pkg2 ...]\n" +"\n" +"apt-cache is a low-level tool used to manipulate APT's binary\n" +"cache files, and query information from them\n" +"\n" +"Commands:\n" +" add - Add a package file to the source cache\n" +" gencaches - Build both the package and source cache\n" +" showpkg - Show some general information for a single package\n" +" showsrc - Show source records\n" +" stats - Show some basic statistics\n" +" dump - Show the entire file in a terse form\n" +" dumpavail - Print an available file to stdout\n" +" unmet - Show unmet dependencies\n" +" search - Search the package list for a regex pattern\n" +" show - Show a readable record for the package\n" +" depends - Show raw dependency information for a package\n" +" rdepends - Show reverse dependency information for a package\n" +" pkgnames - List the names of all packages\n" +" dotty - Generate package graphs for GraphVis\n" +" xvcg - Generate package graphs for xvcg\n" +" policy - Show policy settings\n" +"\n" +"Options:\n" +" -h This help text.\n" +" -p=? The package cache.\n" +" -s=? The source cache.\n" +" -q Disable progress indicator.\n" +" -i Show only important deps for the unmet command.\n" +" -c=? Read this configuration file\n" +" -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" +"See the apt-cache(8) and apt.conf(5) manual pages for more information.\n" +msgstr "" +"Cách sử dụng: apt-cache [tùy_chọn...] lệnh\n" +" apt-cache [tùy_chọn...] add tập_tin1 [tập_tin2 ...]\n" +" apt-cache [tùy_chọn...] showpkg gói1 [gói2 ...]\n" +" apt-cache [tùy_chọn...] showsrc gói1 [gói2 ...]\n" +"(cache: \tbộ nhớ tạm;\n" +"add: \tthêm;\n" +"showpkg: hiển thị gói;\n" +"showsrc: \thiển thị nguồn)\n" +"\n" +"apt-cache là một công cụ mức thấp dùng để thao tác\n" +"những tập tin bộ nhớ tạm nhị phân của APT,\n" +"và cũng để truy vấn thông tin từ những tập tin đó.\n" +"\n" +"Lệnh:\n" +" add\t\t_Thêm_ gói vào bộ nhớ tạm nguồn\n" +" gencaches\tXây dung (_tạo ra_) cả gói lẫn _bộ nhớ tạm_ nguồn đều\n" +" showpkg\t_Hiện_ một phần thông tin chung về một _gói_ riêng lẻ\n" +" showsrc\t_Hiện_ các mục ghi _nguồn_\n" +" stats\t\tHiện một phần _thống kê_ cơ bản\n" +" dump\t\tHiện toàn bộ tập tin dạng ngắn (_đổ_)\n" +" dumpavail\tIn ra một tập tin _sẵn sàng_ vào thiết bị xuất chuẩn (_đổ_)\n" +" unmet\t\tHiện các cách phụ thuộc _chưa thực hiện_\n" +" search\t\t_Tìm kiếm_ mẫu biểu thức chính quy trong danh sách gói\n" +" show\t\t_Hiệnị_ mục ghi có thể đọc, cho những gói đó\n" +" depends\tHiện thông tin cách _phụ thuộc_ thô cho gói\n" +" rdepends\tHiện thông tin cách _phụ thuộc ngược lại_, cho gói\n" +" pkgnames\tHiện danh sách _tên_ mọi _gói_\n" +" dotty\t\tTạo ra đồ thị gói cho GraphVis (_nhiều chấm_)\n" +" xvcg\t\tTạo ra đồ thị gói cho _xvcg_\n" +" policy\t\tHiển thị các thiết lập _chính thức_\n" +"\n" +"Tùy chọn:\n" +" -h \t\t_Trợ giúp_ này\n" +" -p=? \t\tBộ nhớ tạm _gói_.\n" +" -s=? \t\tBộ nhớ tạm _nguồn_.\n" +" -q \t\tTắt cái chỉ tiến trình (_im_).\n" +" -i \t\tHiện chỉ những cách phụ thuộc _quan trọng_\n" +"\t\t\tcho lệnh chưa thực hiện.\n" +" -c=? \t\tĐọc tập tin _cấu hình_ này\n" +" -o=? \t\tLập một tùy chọn cấu hình nhiệm ý, v.d. « -o dir::cache=/tmp »\n" +"Để tìm thông tin thêm thì bạn hãy xem hai trang « man » (hướng dẫn)\n" +"\t\t\tapt-cache(8) và apt.conf(5).\n" + +#: cmdline/apt-cdrom.cc:78 +msgid "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'" +msgstr "Hãy cung cấp tên cho Đĩa này, như « Debian 2.1r1 Đĩa 1 »" + +#: cmdline/apt-cdrom.cc:93 +msgid "Please insert a Disc in the drive and press enter" +msgstr "Hãy nạp đĩa vào ổ và bấm nút Enter" + +#: cmdline/apt-cdrom.cc:117 +msgid "Repeat this process for the rest of the CDs in your set." +msgstr "Hãy lặp lại tiến trình này cho các Đĩa còn lại trong bộ đĩa của bạn." + +#: cmdline/apt-config.cc:41 +msgid "Arguments not in pairs" +msgstr "Không có các đối số dạng cặp" + +#: cmdline/apt-config.cc:76 +msgid "" +"Usage: apt-config [options] command\n" +"\n" +"apt-config is a simple tool to read the APT config file\n" +"\n" +"Commands:\n" +" shell - Shell mode\n" +" dump - Show the configuration\n" +"\n" +"Options:\n" +" -h This help text.\n" +" -c=? Read this configuration file\n" +" -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" +msgstr "" +"Cách sử dụng: apt-config [tùy_chọn...] lệnh\n" +"\n" +"[config: viết tắt cho từ configuration: cấu hình]\n" +"\n" +"apt-config là một công cụ đơn giản để đọc tập tin cấu hình APT.\n" +"\n" +"Lệnh:\n" +" shell\t\tChế độ _hệ vỏ_\n" +" dump\t\tHiển thị cấu hình (_đổ_)\n" +"\n" +"Tùy chọn:\n" +" -h \t\t_Trợ giúp_ này\n" +" -c=? \t\tĐọc tập tin cấu hình này\n" +" -o=? \t\tLập một tùy chọn cấu hình nhiệm ý, v.d. « -o dir::cache=/tmp »\n" + +#: cmdline/apt-extracttemplates.cc:98 +#, c-format +msgid "%s not a valid DEB package." +msgstr "%s không phải là một gói DEB hợp lệ." + +#: cmdline/apt-extracttemplates.cc:232 +msgid "" +"Usage: apt-extracttemplates file1 [file2 ...]\n" +"\n" +"apt-extracttemplates is a tool to extract config and template info\n" +"from debian packages\n" +"\n" +"Options:\n" +" -h This help text\n" +" -t Set the temp dir\n" +" -c=? Read this configuration file\n" +" -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" +msgstr "" +"Cách sử dụng: apt-extracttemplates tập_tin1 [tập_tin2 ...]\n" +"\n" +"[extract: \t\trút;\n" +"templates: \tnhững biểu mẫu]\n" +"\n" +"apt-extracttemplates là một công cụ rút thông tin kiểu cấu hình\n" +"\tvà biểu mẫu đều từ gói Debian\n" +"\n" +"Tùy chọn:\n" +" -h \t\t_Trợ giúp_ này\n" +" -t \t\tLập thư muc tạm thời\n" +"\t\t[temp, tmp: viết tắt cho từ « temporary »: tạm thời]\n" +" -c=? \t\tĐọc tập tin cấu hình này\n" +" -o=? \t\tLập một tùy chọn cấu hình nhiệm ý, v.d. « -o dir::cache=/tmp »\n" + +#: cmdline/apt-extracttemplates.cc:267 apt-pkg/pkgcachegen.cc:710 +#: apt-pkg/pkgcachegen.cc:699 +#, c-format +msgid "Unable to write to %s" +msgstr "Không thể ghi vào %s" + +#: cmdline/apt-extracttemplates.cc:310 +msgid "Cannot get debconf version. Is debconf installed?" +msgstr "Không thể gói phiên bản debconf. Có cài đăt debconf chưa?" + +#: ftparchive/apt-ftparchive.cc:167 ftparchive/apt-ftparchive.cc:341 +#: ftparchive/apt-ftparchive.cc:163 ftparchive/apt-ftparchive.cc:337 +msgid "Package extension list is too long" +msgstr "Danh sách mở rộng gói quá dài" + +#: ftparchive/apt-ftparchive.cc:270 ftparchive/apt-ftparchive.cc:292 +#, c-format +msgid "Error processing directory %s" +msgstr "Gặp lỗi khi xử lý thư mục %s" + +#: ftparchive/apt-ftparchive.cc:254 ftparchive/apt-ftparchive.cc:250 +msgid "Source extension list is too long" +msgstr "Danh sách mở rộng nguồn quá dài" + +#: ftparchive/apt-ftparchive.cc:371 ftparchive/apt-ftparchive.cc:367 +msgid "Error writing header to contents file" +msgstr "Gặp lỗi khi ghi phần đầu vào tập tin nộị dung" + +#: ftparchive/apt-ftparchive.cc:401 +#, c-format +msgid "Error processing contents %s" +msgstr "Gặp lỗi khi xử lý nội dung %s" + +#: ftparchive/apt-ftparchive.cc:556 ftparchive/apt-ftparchive.cc:551 +msgid "" +"Usage: apt-ftparchive [options] command\n" +"Commands: packages binarypath [overridefile [pathprefix]]\n" +" sources srcpath [overridefile [pathprefix]]\n" +" contents path\n" +" release path\n" +" generate config [groups]\n" +" clean config\n" +"\n" +"apt-ftparchive generates index files for Debian archives. It supports\n" +"many styles of generation from fully automated to functional replacements\n" +"for dpkg-scanpackages and dpkg-scansources\n" +"\n" +"apt-ftparchive generates Package files from a tree of .debs. The\n" +"Package file contains the contents of all the control fields from\n" +"each package as well as the MD5 hash and filesize. An override file\n" +"is supported to force the value of Priority and Section.\n" +"\n" +"Similarly apt-ftparchive generates Sources files from a tree of .dscs.\n" +"The --source-override option can be used to specify a src override file\n" +"\n" +"The 'packages' and 'sources' command should be run in the root of the\n" +"tree. BinaryPath should point to the base of the recursive search and \n" +"override file should contain the override flags. Pathprefix is\n" +"appended to the filename fields if present. Example usage from the \n" +"Debian archive:\n" +" apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n" +" dists/potato/main/binary-i386/Packages\n" +"\n" +"Options:\n" +" -h This help text\n" +" --md5 Control MD5 generation\n" +" -s=? Source override file\n" +" -q Quiet\n" +" -d=? Select the optional caching database\n" +" --no-delink Enable delinking debug mode\n" +" --contents Control contents file generation\n" +" -c=? Read this configuration file\n" +" -o=? Set an arbitrary configuration option" +msgstr "" +"Cách sử dụng: apt-ftparchive [tùy_chọn...] lệnh\n" +"\n" +"[ftparchive: FTP archive: kho FTP]\n" +"\n" +"Lệnh: \tpackages binarypath [tập_tin_đè [tiền_tố_đường_dẫn]]\n" +" \tsources srcpath [tập_tin_đè[tiền_tố_đường_dẫn]]\n" +" \tcontents path\n" +" \trelease path\n" +" \tgenerate config [groups]\n" +" \tclean config\n" +"\n" +"[packages: \tnhững gói;\n" +"binarypath: \tđường dẫn nhị phân;\n" +"sources: \t\tnhững nguồn;\n" +"srcpath: \t\tđường dẫn nguồn;\n" +"contents path: đường dẫn nội dụng;\n" +"release path: \tđường dẫn bản đã phát hành;\n" +"generate config [groups]: tạo ra cấu hình [nhóm];\n" +"clean config: \tcấu hình toàn mới)\n" +"\n" +"apt-ftparchive (kho ftp) thì tạo ra tập tin chỉ mục cho kho Debian.\n" +"Nó hỗ trợ nhiều cách tạo ra, từ cách tự động toàn bộ\n" +"đến cách thay thế điều hoặt động cho dpkg-scanpackages (dpkg-quét_gói)\n" +"và dpkg-scansources (dpkg-quét_nguồn).\n" +"\n" +"apt-ftparchive tạo ra tập tin Gói ra cây các .deb.\n" +"Tập tin gói chứa nội dung các trường điều khiển từ mỗi gói,\n" +"cùng với băm MD5 và kích cỡ tập tin.\n" +"Hỗ trợ tập tin đè để buộc giá trị Ưu tiên và Phần\n" +"\n" +"Tương tự, apt-ftparchive tạo ra tập tin Nguồn ra cây các .dsc\n" +"Có thể sử dụng tùy chọn « --source-override » (đè nguồn)\n" +"để ghi rõ tập tin đè nguồn\n" +"\n" +"Lnh « packages » (gói) và « sources » (nguồn) nên chạy tại gốc cây.\n" +"BinaryPath (đường dẫn nhị phân) nên chỉ tới cơ bản của việc tìm kiếm đệ " +"quy,\n" +"và tập tin đè nên chứa những cờ đè.\n" +"Pathprefix (tiền tố đường dẫn) được phụ thêm vào\n" +"những trường tên tập tin nếu có.\n" +"Cách sử dụng thí dụ từ kho Debian:\n" +" apt-ftparchive packages dists/potato/main/binary-i386/ > \\\n" +" dists/potato/main/binary-i386/Packages\n" +"\n" +"Tùy chọn:\n" +" -h \t\t_Trợ giúp_ này\n" +" --md5 \t\tĐiều khiển cách tạo ra MD5\n" +" -s=? \t\tTập tin đè nguồn\n" +" -q \t\t_Im_ (không xuất chi tiết)\n" +" -d=? \t\tChọn _cơ sở dữ liệu_ nhớ tạm tùy chọn\n" +" --no-delink \tMở chế độ gỡ lỗi _bỏ liên kết_\n" +" --contents \tĐiều khiển cách tạo ra tập tin _nội dung_\n" +" -c=? \t\tĐọc tập tin cấu hình này\n" +" -o=? \t\tLập một tùy chọn cấu hình nhiệm ý, v.d. « -o dir::cache=/tmp »" + +#: ftparchive/apt-ftparchive.cc:762 ftparchive/apt-ftparchive.cc:757 +msgid "No selections matched" +msgstr "Chưa khớp điều đã chọn nào." + +#: ftparchive/apt-ftparchive.cc:835 ftparchive/apt-ftparchive.cc:830 +#, c-format +msgid "Some files are missing in the package file group `%s'" +msgstr "Thiếu một số tập tin trong nhóm tập tin gói « %s »" + +#: ftparchive/cachedb.cc:45 +#, c-format +msgid "DB was corrupted, file renamed to %s.old" +msgstr "Cơ sở dữ liệu bị hỏng nên đã đổi tên tâp tin thành %s.old (old: cũ)." + +#: ftparchive/cachedb.cc:63 +#, c-format +msgid "DB is old, attempting to upgrade %s" +msgstr "Cơ sở dữ liệu cũ nên đang cố nâng cấp lên %s" + +#: ftparchive/cachedb.cc:73 +#, c-format +msgid "Unable to open DB file %s: %s" +msgstr "Không thể mở tập tin cơ sở dữ liệu %s: %s." + +#: ftparchive/cachedb.cc:114 +#, c-format +msgid "File date has changed %s" +msgstr "Ngày tập tin đã đổi %s" + +#: ftparchive/cachedb.cc:155 +msgid "Archive has no control record" +msgstr "Kho không có mục ghi điều khiển" + +#: ftparchive/cachedb.cc:267 +msgid "Unable to get a cursor" +msgstr "Không thể lấy con chạy" + +#: ftparchive/writer.cc:78 ftparchive/writer.cc:79 +#, c-format +msgid "W: Unable to read directory %s\n" +msgstr "W: Không thể đọc thư mục %s\n" + +#: ftparchive/writer.cc:83 ftparchive/writer.cc:84 +#, c-format +msgid "W: Unable to stat %s\n" +msgstr "W: Không thể lấy thông tin toàn bộ cho %s\n" + +#: ftparchive/writer.cc:125 ftparchive/writer.cc:126 +msgid "E: " +msgstr "E: " + +#: ftparchive/writer.cc:127 ftparchive/writer.cc:128 +msgid "W: " +msgstr "W: " + +#: ftparchive/writer.cc:134 ftparchive/writer.cc:135 +msgid "E: Errors apply to file " +msgstr "E: có lỗi áp dụng vào tập tin" + +#: ftparchive/writer.cc:151 ftparchive/writer.cc:181 ftparchive/writer.cc:152 +#: ftparchive/writer.cc:182 +#, c-format +msgid "Failed to resolve %s" +msgstr "Việc quyết định %s bị lỗi" + +#: ftparchive/writer.cc:163 ftparchive/writer.cc:164 +msgid "Tree walking failed" +msgstr "Việc di chuyển qua cây bị lỗi" + +#: ftparchive/writer.cc:188 ftparchive/writer.cc:189 +#, c-format +msgid "Failed to open %s" +msgstr "Việc mở %s bị lỗi" + +#: ftparchive/writer.cc:245 ftparchive/writer.cc:246 +#, c-format +msgid " DeLink %s [%s]\n" +msgstr " Bỏ liên kết %s [%s]\n" + +#: ftparchive/writer.cc:253 ftparchive/writer.cc:254 +#, c-format +msgid "Failed to readlink %s" +msgstr "Việc tạo liên kết lại %s bị lỗi" + +#: ftparchive/writer.cc:257 ftparchive/writer.cc:258 +#, c-format +msgid "Failed to unlink %s" +msgstr "Việc bỏ liên kết %s bị lỗi" + +#: ftparchive/writer.cc:264 ftparchive/writer.cc:265 +#, c-format +msgid "*** Failed to link %s to %s" +msgstr "*** Việc liên kết %s đến %s bị lỗi" + +#: ftparchive/writer.cc:274 ftparchive/writer.cc:275 +#, c-format +msgid " DeLink limit of %sB hit.\n" +msgstr " Hết hạn bỏ liên kết của %sB.\n" + +#: ftparchive/writer.cc:358 apt-inst/extract.cc:181 apt-inst/extract.cc:193 +#: apt-inst/extract.cc:210 apt-inst/deb/dpkgdb.cc:121 methods/gpgv.cc:260 +#, c-format +msgid "Failed to stat %s" +msgstr "Việc lấy thông tin toàn bộ cho %s bị lỗi" + +#: ftparchive/writer.cc:386 ftparchive/writer.cc:378 +msgid "Archive had no package field" +msgstr "Kho không có trường gói" + +#: ftparchive/writer.cc:394 ftparchive/writer.cc:603 ftparchive/writer.cc:386 +#: ftparchive/writer.cc:595 +#, c-format +msgid " %s has no override entry\n" +msgstr " %s không có mục ghi đè\n" + +#: ftparchive/writer.cc:437 ftparchive/writer.cc:689 ftparchive/writer.cc:429 +#: ftparchive/writer.cc:677 +#, c-format +msgid " %s maintainer is %s not %s\n" +msgstr " người bảo quản %s là %s không phải %s\n" + +#: ftparchive/contents.cc:317 +#, c-format +msgid "Internal error, could not locate member %s" +msgstr "Gặp lỗi nội bộ, không thể định vị bộ phạn %s" + +#: ftparchive/contents.cc:353 ftparchive/contents.cc:384 +#: ftparchive/contents.cc:346 ftparchive/contents.cc:377 +msgid "realloc - Failed to allocate memory" +msgstr "realloc (cấp phát lại) - việc cấp phát bộ nhớ bị lỗi" + +#: ftparchive/override.cc:38 ftparchive/override.cc:146 +#, c-format +msgid "Unable to open %s" +msgstr "Không thể mở %s" + +#: ftparchive/override.cc:64 ftparchive/override.cc:170 +#, c-format +msgid "Malformed override %s line %lu #1" +msgstr "Điều đè dạng sai %s dòng %lu #1" + +#: ftparchive/override.cc:78 ftparchive/override.cc:182 +#, c-format +msgid "Malformed override %s line %lu #2" +msgstr "Điều đè dạng sai %s dòng %lu #2" + +#: ftparchive/override.cc:92 ftparchive/override.cc:195 +#, c-format +msgid "Malformed override %s line %lu #3" +msgstr "Điều đè dạng sai %s dòng %lu #3" + +#: ftparchive/override.cc:131 ftparchive/override.cc:205 +#, c-format +msgid "Failed to read the override file %s" +msgstr "Việc đọc tập tin đè %s bị lỗi" + +#: ftparchive/multicompress.cc:75 +#, c-format +msgid "Unknown compression algorithm '%s'" +msgstr "Không biết thuật toán nén « %s »" + +#: ftparchive/multicompress.cc:105 +#, c-format +msgid "Compressed output %s needs a compression set" +msgstr "Dữ liệu xuất đã nén %s cần một bộ nén" + +#: ftparchive/multicompress.cc:172 methods/rsh.cc:91 +msgid "Failed to create IPC pipe to subprocess" +msgstr "Việc tạo ống IPC đến tiến trình con bị lỗi" + +#: ftparchive/multicompress.cc:198 +msgid "Failed to create FILE*" +msgstr "Việc tạo TẬP_TIN* bị lỗi" + +#: ftparchive/multicompress.cc:201 +msgid "Failed to fork" +msgstr "Việc tạo tiến trình con bị lỗi" + +#: ftparchive/multicompress.cc:215 +msgid "Compress child" +msgstr "Nén điều con" + +#: ftparchive/multicompress.cc:238 +#, c-format +msgid "Internal error, failed to create %s" +msgstr "Lỗi nội bộ, việc tạo %s bị lỗi" + +#: ftparchive/multicompress.cc:289 +msgid "Failed to create subprocess IPC" +msgstr "Việc tạo tiến trình con IPC bị lỗi" + +#: ftparchive/multicompress.cc:324 +msgid "Failed to exec compressor " +msgstr "Việc thực hiện bô nén bị lỗi " + +#: ftparchive/multicompress.cc:363 +msgid "decompressor" +msgstr "bộ giải nén" + +#: ftparchive/multicompress.cc:406 +msgid "IO to subprocess/file failed" +msgstr "việc nhập/xuất vào tiến trình con/tập tin bị lỗi" + +#: ftparchive/multicompress.cc:458 +msgid "Failed to read while computing MD5" +msgstr "Việc đọc khi tính MD5 bị lỗi" + +#: ftparchive/multicompress.cc:475 +#, c-format +msgid "Problem unlinking %s" +msgstr "Gặp lỗi khi bỏ liên kết %s" + +#: ftparchive/multicompress.cc:490 apt-inst/extract.cc:188 +#, c-format +msgid "Failed to rename %s to %s" +msgstr "Việc đổi tên %s thành %s bị lỗi" + +#: ../app/widgets/gimpcursorview.c:198 ../src/runtime-config.c:72 +#: ../lib/foocanvas/libfoocanvas/foo-canvas.c:1233 +#: ../lib/foocanvas/libfoocanvas/foo-canvas.c:1234 libexif/exif-entry.c:810 +#, fuzzy +msgid "Y" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"C\n" +"#-#-#-#-# guikachu.vi.po (guikachu HEAD) #-#-#-#-#\n" +"Y\n" +"#-#-#-#-# libexif-0.6.13.vi.po (libexif-0.6.13) #-#-#-#-#\n" +"Y" + +#: cmdline/apt-get.cc:142 cmdline/apt-get.cc:1515 cmdline/apt-get.cc:140 +#: cmdline/apt-get.cc:1422 +#, c-format +msgid "Regex compilation error - %s" +msgstr "Lỗi biên dich biểu thức chính quy - %s" + +#: cmdline/apt-get.cc:237 cmdline/apt-get.cc:235 +msgid "The following packages have unmet dependencies:" +msgstr "Những gói theo đây phụ thuộc vào phần mềm chưa có :" + +#: cmdline/apt-get.cc:327 cmdline/apt-get.cc:325 +#, c-format +msgid "but %s is installed" +msgstr "nhưng mà %s đã được cài đặt" + +#: cmdline/apt-get.cc:329 cmdline/apt-get.cc:327 +#, c-format +msgid "but %s is to be installed" +msgstr "nhưng mà %s sẽ được cài đặt" + +#: cmdline/apt-get.cc:336 src/cmdline/cmdline_prompt.cc:231 +#: src/cmdline/cmdline_show_broken.cc:83 cmdline/apt-get.cc:334 +#, c-format +msgid "but it is not installable" +msgstr "nhưng mà nó không có khả năng cài đặt" + +#: cmdline/apt-get.cc:338 cmdline/apt-get.cc:336 +msgid "but it is a virtual package" +msgstr "nhưng mà nó là gói ảo" + +#: cmdline/apt-get.cc:341 cmdline/apt-get.cc:339 +msgid "but it is not installed" +msgstr "nhưng mà nó chưa được cài đặt" + +#: cmdline/apt-get.cc:341 cmdline/apt-get.cc:339 +msgid "but it is not going to be installed" +msgstr "nhưng mà nó sẽ không được cài đặt" + +#: cmdline/apt-get.cc:346 src/cmdline/cmdline_prompt.cc:238 +#: src/cmdline/cmdline_show_broken.cc:90 cmdline/apt-get.cc:344 +#, c-format +msgid " or" +msgstr " hay" + +#: cmdline/apt-get.cc:375 src/cmdline/cmdline_prompt.cc:258 +#: cmdline/apt-get.cc:373 +msgid "The following NEW packages will be installed:" +msgstr "Theo đây có những gói MỚI sẽ được cài đặt:" + +#: cmdline/apt-get.cc:401 src/cmdline/cmdline_prompt.cc:259 +#: cmdline/apt-get.cc:399 +msgid "The following packages will be REMOVED:" +msgstr "Theo đây có những gói sẽ bị GỠ BỎ :" + +#: cmdline/apt-get.cc:423 src/cmdline/cmdline_prompt.cc:256 +#: cmdline/apt-get.cc:421 +msgid "The following packages have been kept back:" +msgstr "Theo đây có những gói đã được giữ lại:" + +#: cmdline/apt-get.cc:444 src/cmdline/cmdline_prompt.cc:260 +#: cmdline/apt-get.cc:442 +msgid "The following packages will be upgraded:" +msgstr "Theo đây có những gói sẽ được nâng cấp:" + +#: cmdline/apt-get.cc:465 src/cmdline/cmdline_prompt.cc:255 +#: cmdline/apt-get.cc:463 +msgid "The following packages will be DOWNGRADED:" +msgstr "Theo đây có những gói sẽ được HẠ CẤP:" + +#: cmdline/apt-get.cc:485 cmdline/apt-get.cc:483 +msgid "The following held packages will be changed:" +msgstr "Theo đây có những gói sẽ được thay đổi:" + +#: cmdline/apt-get.cc:538 cmdline/apt-get.cc:536 +#, c-format +msgid "%s (due to %s) " +msgstr "%s (do %s)" + +#: cmdline/apt-get.cc:546 +msgid "" +"WARNING: The following essential packages will be removed.\n" +"This should NOT be done unless you know exactly what you are doing!" +msgstr "" +"CẢNH BÁO : theo đây có những gói chủ yếu sẽ bị gỡ bỏ.\n" +"ĐỪNG làm như thế trừ khi bạn biết làm gì ở đây nó một cách chính xác." + +#: cmdline/apt-get.cc:577 cmdline/apt-get.cc:574 +#, c-format +msgid "%lu upgraded, %lu newly installed, " +msgstr "%lu đã nâng cấp, %lu mới được cài đặt, " + +#: cmdline/apt-get.cc:581 src/cmdline/cmdline_prompt.cc:516 +#: cmdline/apt-get.cc:578 +#, c-format +msgid "%lu reinstalled, " +msgstr "%lu được cài đặt lại, " + +#: cmdline/apt-get.cc:583 src/cmdline/cmdline_prompt.cc:518 +#: cmdline/apt-get.cc:580 +#, c-format +msgid "%lu downgraded, " +msgstr "%lu được hạ cấp, " + +#: cmdline/apt-get.cc:585 src/cmdline/cmdline_prompt.cc:520 +#: cmdline/apt-get.cc:582 +#, c-format +msgid "%lu to remove and %lu not upgraded.\n" +msgstr "%lu cần gỡ bỏ, và %lu chưa được nâng cấp.\n" + +#: cmdline/apt-get.cc:589 cmdline/apt-get.cc:586 +#, c-format +msgid "%lu not fully installed or removed.\n" +msgstr "%lu chưa được cài đặt toàn bộ hay được gỡ bỏ.\n" + +#: cmdline/apt-get.cc:649 cmdline/apt-get.cc:646 +msgid "Correcting dependencies..." +msgstr "Đang sửa cách phụ thuộc..." + +#: cmdline/apt-get.cc:652 cmdline/apt-get.cc:649 +msgid " failed." +msgstr " đã thất bại." + +#: cmdline/apt-get.cc:655 cmdline/apt-get.cc:652 +msgid "Unable to correct dependencies" +msgstr "Không thể sửa cách phụ thuộc" + +#: cmdline/apt-get.cc:658 cmdline/apt-get.cc:655 +msgid "Unable to minimize the upgrade set" +msgstr "Không thể cực tiểu hóa bộ nâng cấp" + +#: cmdline/apt-get.cc:660 cmdline/apt-get.cc:657 +msgid " Done" +msgstr " Đã xong" + +#: cmdline/apt-get.cc:664 cmdline/apt-get.cc:661 +msgid "You might want to run `apt-get -f install' to correct these." +msgstr "Có lẽ bạn hãy chay lệnh « apt-get -f install » để sửa hết." + +#: cmdline/apt-get.cc:667 cmdline/apt-get.cc:664 +msgid "Unmet dependencies. Try using -f." +msgstr "" +"Còn có cách phụ thuộc vào phần mềm chưa có. Như thế thì bạn hãy cố dùng tùy " +"chọn « -f »." + +#: cmdline/apt-get.cc:689 +msgid "WARNING: The following packages cannot be authenticated!" +msgstr "CẢNH BÁO : không thể xác thực những gói theo đây." + +#: cmdline/apt-get.cc:693 +msgid "Authentication warning overridden.\n" +msgstr "Cảnh báo xác thực bị đè.\n" + +#: cmdline/apt-get.cc:700 +msgid "Install these packages without verification [y/N]? " +msgstr "Cài đặt những gói này mà không kiểm chứng không? [y/N] [c/K] " + +#: cmdline/apt-get.cc:702 +msgid "Some packages could not be authenticated" +msgstr "Một số gói không thể được xác thực" + +#: cmdline/apt-get.cc:711 cmdline/apt-get.cc:858 cmdline/apt-get.cc:811 +msgid "There are problems and -y was used without --force-yes" +msgstr "Gập lỗi và đã dùng tùy chọn « -y » mà không có « --force-yes »" + +#: cmdline/apt-get.cc:755 +msgid "Internal error, InstallPackages was called with broken packages!" +msgstr "Lỗi nội bộ: InstallPackages (cài đặt gói) được gọi với gói bị hỏng." + +#: cmdline/apt-get.cc:764 +msgid "Packages need to be removed but remove is disabled." +msgstr "Cần phải gỡ bỏ một số gói, nhưng mà khả năng Gỡ bỏ (Remove) đã bị tắt." + +#: cmdline/apt-get.cc:775 +msgid "Internal error, Ordering didn't finish" +msgstr "Gặp lỗi nội bộ: tiến trình Sắp xếp chưa xong" + +#: cmdline/apt-get.cc:791 cmdline/apt-get.cc:1809 cmdline/apt-get.cc:1842 +#: cmdline/apt-get.cc:744 cmdline/apt-get.cc:1716 cmdline/apt-get.cc:1749 +msgid "Unable to lock the download directory" +msgstr "Không thể khoá thư mục tải về" + +#: cmdline/apt-get.cc:2008 +msgid "The list of sources could not be read." +msgstr "Không thể đọc danh sách nguồn." + +#: cmdline/apt-get.cc:816 +msgid "How odd.. The sizes didn't match, email apt@packages.debian.org" +msgstr "" +"Lạ... Hai kích cỡ không khớp được. Hãy gởi thư cho " + +#: cmdline/apt-get.cc:821 cmdline/apt-get.cc:774 +#, c-format +msgid "Need to get %sB/%sB of archives.\n" +msgstr "Cần phải lấy %sB/%sB kho.\n" + +#: cmdline/apt-get.cc:824 cmdline/apt-get.cc:777 +#, c-format +msgid "Need to get %sB of archives.\n" +msgstr "Cần phải lấy %sB kho.\n" + +#: cmdline/apt-get.cc:829 cmdline/apt-get.cc:782 +#, c-format +msgid "After unpacking %sB of additional disk space will be used.\n" +msgstr "Sau khi đã giải nén, sẻ chiếm %sB sức chứa đĩa thêm.\n" + +#: cmdline/apt-get.cc:832 cmdline/apt-get.cc:785 +#, c-format +msgid "After unpacking %sB disk space will be freed.\n" +msgstr "Sau khi đã giải nén, sẽ giải phóng %sB sức chữa đĩa thêm.\n" + +#: cmdline/apt-get.cc:846 cmdline/apt-get.cc:1980 +#, c-format +msgid "Couldn't determine free space in %s" +msgstr "Không thể quyết định chỗ rảnh trong %s" + +#: cmdline/apt-get.cc:849 cmdline/apt-get.cc:802 +#, c-format +msgid "You don't have enough free space in %s." +msgstr "Bạn chưa có đủ sức chức còn rảnh trong %s." + +#: cmdline/apt-get.cc:864 cmdline/apt-get.cc:884 cmdline/apt-get.cc:817 +#: cmdline/apt-get.cc:837 +msgid "Trivial Only specified but this is not a trivial operation." +msgstr "" +"Xác đinh « Chỉ không đáng kể » (Trivial Only) nhưng mà thao tác này đáng kể." + +#: cmdline/apt-get.cc:866 cmdline/apt-get.cc:819 +msgid "Yes, do as I say!" +msgstr "Có, làm đi." + +#: cmdline/apt-get.cc:868 +#, c-format +msgid "" +"You are about to do something potentially harmful.\n" +"To continue type in the phrase '%s'\n" +" ?] " +msgstr "" +"Bạn sắp làm gì có thể có hai.\n" +"Để tiếp tục thì hãy gõ cụm từ « %s »\n" +"?]" + +#: cmdline/apt-get.cc:874 cmdline/apt-get.cc:893 cmdline/apt-get.cc:827 +#: cmdline/apt-get.cc:846 +msgid "Abort." +msgstr "Hủy bỏ." + +#: cmdline/apt-get.cc:889 +msgid "Do you want to continue [Y/n]? " +msgstr "Bạn có muốn tiếp tục không? [Y/n] [C/k] " + +#: cmdline/apt-get.cc:961 cmdline/apt-get.cc:1365 cmdline/apt-get.cc:2023 +#: cmdline/apt-get.cc:911 cmdline/apt-get.cc:1281 cmdline/apt-get.cc:1906 +#, c-format +msgid "Failed to fetch %s %s\n" +msgstr "Việc gói %s bị lỗi %s\n" + +#: cmdline/apt-get.cc:979 cmdline/apt-get.cc:929 +msgid "Some files failed to download" +msgstr "Một số tập tin không tải về được" + +#: cmdline/apt-get.cc:980 cmdline/apt-get.cc:2032 cmdline/apt-get.cc:930 +#: cmdline/apt-get.cc:1915 +msgid "Download complete and in download only mode" +msgstr "Mới tải về xong và trong chế độ chỉ tải về" + +#: cmdline/apt-get.cc:986 cmdline/apt-get.cc:936 +msgid "" +"Unable to fetch some archives, maybe run apt-get update or try with --fix-" +"missing?" +msgstr "" +"Không thể lấy một số kho, có lẽ hãy chạy lệnh « apt-get update » (apt lấy " +"cập nhật) hay cố với « --fix-missing » (sửa các điều còn thiếu) không?" + +#: cmdline/apt-get.cc:990 cmdline/apt-get.cc:940 +msgid "--fix-missing and media swapping is not currently supported" +msgstr "" +"Chưa hô trợ tùy chọn « --fix-missing » (sửa khi thiếu điều) và trao đổi " +"phương tiện" + +#: cmdline/apt-get.cc:995 cmdline/apt-get.cc:945 +msgid "Unable to correct missing packages." +msgstr "Không thể sửa những gói còn thiếu." + +#: cmdline/apt-get.cc:996 +msgid "Aborting install." +msgstr "Đang hủy bỏ cài đặt." + +#: cmdline/apt-get.cc:1030 cmdline/apt-get.cc:979 +#, c-format +msgid "Note, selecting %s instead of %s\n" +msgstr "Ghi chú : đang chọn %s thay vì %s\n" + +#: cmdline/apt-get.cc:1040 cmdline/apt-get.cc:989 +#, c-format +msgid "Skipping %s, it is already installed and upgrade is not set.\n" +msgstr "Đang bỏ qua %s vì nó đã được cài đặt và chưa lập tùy chọn Nâng cấp.\n" + +#: cmdline/apt-get.cc:1058 cmdline/apt-get.cc:1007 +#, c-format +msgid "Package %s is not installed, so not removed\n" +msgstr "Chưa cài đặt gói %s nên không thể gỡ bỏ nó\n" + +#: cmdline/apt-get.cc:1069 cmdline/apt-get.cc:1018 +#, c-format +msgid "Package %s is a virtual package provided by:\n" +msgstr "Gói %s là gói ảo được cung cấp do :\n" + +#: cmdline/apt-get.cc:1081 cmdline/apt-get.cc:1030 +msgid " [Installed]" +msgstr " [Đã cài đặt]" + +#: cmdline/apt-get.cc:1086 cmdline/apt-get.cc:1035 +msgid "You should explicitly select one to install." +msgstr "Bạn nên chọn một cách dứt khoát gói cần cài." + +#: cmdline/apt-get.cc:1091 cmdline/apt-get.cc:1040 +#, c-format +msgid "" +"Package %s is not available, but is referred to by another package.\n" +"This may mean that the package is missing, has been obsoleted, or\n" +"is only available from another source\n" +msgstr "" +"Gói %s không phải sẵn sàng, nhưng mà một gói khác\n" +"đã tham chiếu đến nó. Có lẽ có nghĩa là gói còn thiếu,\n" +"đã trở thành cũ, hay chỉ sẵn sàng từ nguồn khác.\n" + +#: cmdline/apt-get.cc:1110 cmdline/apt-get.cc:1059 +msgid "However the following packages replace it:" +msgstr "Tuy nhiên, những gói theo đây thay thế nó :" + +#: cmdline/apt-get.cc:1113 cmdline/apt-get.cc:1062 +#, c-format +msgid "Package %s has no installation candidate" +msgstr "Gói %s không có ứng cử cài đặt" + +#: cmdline/apt-get.cc:1133 cmdline/apt-get.cc:1082 +#, c-format +msgid "Reinstallation of %s is not possible, it cannot be downloaded.\n" +msgstr "Không thể cài đặt lại %s vì không thể tải về nó.\n" + +#: cmdline/apt-get.cc:1141 cmdline/apt-get.cc:1090 +#, c-format +msgid "%s is already the newest version.\n" +msgstr "%s là phiên bản mơi nhất.\n" + +#: cmdline/apt-get.cc:1168 cmdline/apt-get.cc:1117 +#, c-format +msgid "Release '%s' for '%s' was not found" +msgstr "Không tìm thấy bản phát hành « %s » cho « %s »" + +#: cmdline/apt-get.cc:1170 cmdline/apt-get.cc:1119 +#, c-format +msgid "Version '%s' for '%s' was not found" +msgstr "Không tìm thấy phiên bản « %s » cho « %s »" + +#: cmdline/apt-get.cc:1176 cmdline/apt-get.cc:1125 +#, c-format +msgid "Selected version %s (%s) for %s\n" +msgstr "Đã chọn phiên bản %s (%s) cho %s\n" + +#: cmdline/apt-get.cc:1313 cmdline/apt-get.cc:1235 +msgid "The update command takes no arguments" +msgstr "Lệnh cập nhật không chấp nhật đối số" + +#: cmdline/apt-get.cc:1326 cmdline/apt-get.cc:1420 cmdline/apt-get.cc:1248 +msgid "Unable to lock the list directory" +msgstr "Không thể khoá thư mục danh sách" + +#: cmdline/apt-get.cc:1384 cmdline/apt-get.cc:1300 +msgid "" +"Some index files failed to download, they have been ignored, or old ones " +"used instead." +msgstr "" +"Một số tập tin chỉ mục không tải về được, đã bỏ qua chúng, hoặc điều cũ được " +"dùng thay thế." + +#: cmdline/apt-get.cc:1403 +msgid "Internal error, AllUpgrade broke stuff" +msgstr "Lỗi nội bộ: AllUpgrade (toàn bộ nâng cấp) đã ngắt gì" + +#: cmdline/apt-get.cc:1502 cmdline/apt-get.cc:1538 cmdline/apt-get.cc:1409 +#: cmdline/apt-get.cc:1445 +#, c-format +msgid "Couldn't find package %s" +msgstr "Không tìm thấy gói %s" + +#: cmdline/apt-get.cc:1525 cmdline/apt-get.cc:1432 +#, c-format +msgid "Note, selecting %s for regex '%s'\n" +msgstr "Ghi chú : đang chọn %s cho biểu thức chính quy « %s »\n" + +#: cmdline/apt-get.cc:1555 cmdline/apt-get.cc:1462 +msgid "You might want to run `apt-get -f install' to correct these:" +msgstr "Có lẽ bạn hãy chạy lênh « apt-get -f install » để sửa hết:" + +#: cmdline/apt-get.cc:1558 cmdline/apt-get.cc:1465 +msgid "" +"Unmet dependencies. Try 'apt-get -f install' with no packages (or specify a " +"solution)." +msgstr "" +"Gói còn phụ thuộc vào phần mềm chưa có. Hãy cố chạy lệnh « apt-get -f " +"install » mà không có gói nào (hoặc ghi rõ cách quyết định)." + +#: cmdline/apt-get.cc:1570 cmdline/apt-get.cc:1477 +msgid "" +"Some packages could not be installed. This may mean that you have\n" +"requested an impossible situation or if you are using the unstable\n" +"distribution that some required packages have not yet been created\n" +"or been moved out of Incoming." +msgstr "" +"Không thể cài đặt một số gói. Có lẽ có nghĩa là bạn đa yêu cầu\n" +"một trường hợp không thể, hoặc nếu bạn sử dụng bản phân phối\n" +"bất định, có lẽ chưa tạo một số gói cần thiết,\n" +"hoặc chưa di chuyển chúng ra phần Incoming (Đến)." + +#: cmdline/apt-get.cc:1578 cmdline/apt-get.cc:1485 +msgid "" +"Since you only requested a single operation it is extremely likely that\n" +"the package is simply not installable and a bug report against\n" +"that package should be filed." +msgstr "" +"Vì bạn đã yêu cầu chỉ một thao tác riêng lẻ, rât có thể là\n" +"gói này đơn giản không có khả năng cài đặt, thì bạn hay\n" +"thông báo lỗi về gói này." + +#: cmdline/apt-get.cc:1583 cmdline/apt-get.cc:1490 +msgid "The following information may help to resolve the situation:" +msgstr "Có lẽ thông tin theo đây sẽ giúp đỡ quyết định trường hợp:" + +#: cmdline/apt-get.cc:1586 cmdline/apt-get.cc:1493 +msgid "Broken packages" +msgstr "Gói bị ngắt" + +#: cmdline/apt-get.cc:1612 cmdline/apt-get.cc:1519 +msgid "The following extra packages will be installed:" +msgstr "Những gói thêm theo đây sẽ được cài đặt:" + +#: cmdline/apt-get.cc:1683 cmdline/apt-get.cc:1590 +msgid "Suggested packages:" +msgstr "Gói được đệ nghị:" + +#: cmdline/apt-get.cc:1684 cmdline/apt-get.cc:1591 +msgid "Recommended packages:" +msgstr "Gói được khuyên:" + +#: cmdline/apt-get.cc:1704 +msgid "Calculating upgrade... " +msgstr "Đang tính nâng cấp... " + +#: cmdline/apt-get.cc:1707 methods/ftp.cc:702 methods/connect.cc:101 +#: cmdline/apt-get.cc:1614 methods/connect.cc:99 src/common/dcc.c:68 +msgid "Failed" +msgstr "Bị lỗi" + +#: cmdline/apt-get.cc:1712 ../mail/mail-config.glade.h:72 main.c:175 +#: src/common/dcc.c:70 cmdline/apt-get.cc:1619 src/common/dcc.c:69 +#: ../scripts/pybtext.py:344 datebook_gui.c:1326 import_gui.c:311 +#: prefs_gui.c:763 +msgid "Done" +msgstr "Đã xong" + +#: cmdline/apt-get.cc:1777 cmdline/apt-get.cc:1785 +msgid "Internal error, problem resolver broke stuff" +msgstr "Lỗi nội bộ: bộ tháo gỡ vấn đề đã ngắt gì" + +#: cmdline/apt-get.cc:1885 cmdline/apt-get.cc:1792 +msgid "Must specify at least one package to fetch source for" +msgstr "Phải ghi rõ ít nhất một gói cần lấy nguồn cho nó" + +#: cmdline/apt-get.cc:1915 cmdline/apt-get.cc:2144 cmdline/apt-get.cc:1819 +#: cmdline/apt-get.cc:2026 +#, c-format +msgid "Unable to find a source package for %s" +msgstr "Không tìm thấy gói nguồn cho %s" + +#: cmdline/apt-get.cc:1959 +#, c-format +msgid "Skipping already downloaded file '%s'\n" +msgstr "Đang bỏ qua tập tin đã được tải về « %s »\n" + +#: cmdline/apt-get.cc:1983 cmdline/apt-get.cc:1866 +#, c-format +msgid "You don't have enough free space in %s" +msgstr "Không đủ sức chứa còn rảnh trong %s" + +#: cmdline/apt-get.cc:1988 cmdline/apt-get.cc:1871 +#, c-format +msgid "Need to get %sB/%sB of source archives.\n" +msgstr "Cần phải lấy %sB/%sB kho nguồn.\n" + +#: cmdline/apt-get.cc:1991 cmdline/apt-get.cc:1874 +#, c-format +msgid "Need to get %sB of source archives.\n" +msgstr "Cần phải lấy %sB kho nguồn.\n" + +#: cmdline/apt-get.cc:1997 +#, c-format +msgid "Fetch source %s\n" +msgstr "Lấy nguồn %s\n" + +#: cmdline/apt-get.cc:2028 cmdline/apt-get.cc:1911 +msgid "Failed to fetch some archives." +msgstr "Việc lấy một số kho bị lỗi." + +#: cmdline/apt-get.cc:2056 cmdline/apt-get.cc:1939 +#, c-format +msgid "Skipping unpack of already unpacked source in %s\n" +msgstr "Đang bỏ qua giải nén nguồn đã giải nén trong %s\n" + +#: cmdline/apt-get.cc:2068 cmdline/apt-get.cc:1951 +#, c-format +msgid "Unpack command '%s' failed.\n" +msgstr "Lệnh giải nén « %s » bị lỗi.\n" + +#: cmdline/apt-get.cc:2069 +#, c-format +msgid "Check if the 'dpkg-dev' package is installed.\n" +msgstr "Hãy kiểm tra xem gói « dpkg-dev » có được cài đặt chưa.\n" + +#: cmdline/apt-get.cc:2086 cmdline/apt-get.cc:1968 +#, c-format +msgid "Build command '%s' failed.\n" +msgstr "Lệnh xây dụng « %s » bị lỗi.\n" + +#: cmdline/apt-get.cc:2105 cmdline/apt-get.cc:1987 +msgid "Child process failed" +msgstr "Tiến trình con bị lỗi" + +#: cmdline/apt-get.cc:2121 cmdline/apt-get.cc:2003 +msgid "Must specify at least one package to check builddeps for" +msgstr "" +"Phải ghi rõ ít nhất một gói cần kiểm tra cách phụ thuộc khi xây dụng cho nó" + +#: cmdline/apt-get.cc:2149 cmdline/apt-get.cc:2031 +#, c-format +msgid "Unable to get build-dependency information for %s" +msgstr "Không thể lấy thông tin về cách phụ thuộc khi xây dụng cho %s" + +#: cmdline/apt-get.cc:2169 cmdline/apt-get.cc:2051 +#, c-format +msgid "%s has no build depends.\n" +msgstr "%s không phụ thuộc vào gì khi xây dụng.\n" + +#: cmdline/apt-get.cc:2221 cmdline/apt-get.cc:2103 +#, c-format +msgid "" +"%s dependency for %s cannot be satisfied because the package %s cannot be " +"found" +msgstr "cách phụ thuộc %s cho %s không thể được thỏa vì không tìm thấy gọi %s" + +#: cmdline/apt-get.cc:2273 cmdline/apt-get.cc:2155 +#, c-format +msgid "" +"%s dependency for %s cannot be satisfied because no available versions of " +"package %s can satisfy version requirements" +msgstr "" +"cách phụ thuộc %s cho %s không thể được thỏa vì không có phiên bản sẵn sàng " +"của gói %s có thể thỏa điều kiện phiên bản" + +#: cmdline/apt-get.cc:2308 cmdline/apt-get.cc:2190 +#, c-format +msgid "Failed to satisfy %s dependency for %s: Installed package %s is too new" +msgstr "" +"Việc cố thỏa cách phụ thuộc %s cho %s bị lỗi vì gói đã cài đặt %s quá mới" + +#: cmdline/apt-get.cc:2333 cmdline/apt-get.cc:2215 +#, c-format +msgid "Failed to satisfy %s dependency for %s: %s" +msgstr "Việc cố thỏa cách phụ thuộc %s cho %s bị lỗi: %s" + +#: cmdline/apt-get.cc:2347 cmdline/apt-get.cc:2229 +#, c-format +msgid "Build-dependencies for %s could not be satisfied." +msgstr "Không thể thỏa cách phụ thuộc khi xây dụng cho %s." + +#: cmdline/apt-get.cc:2351 cmdline/apt-get.cc:2233 +msgid "Failed to process build dependencies" +msgstr "Việc xử lý cách phụ thuộc khi xây dụng bị lỗi" + +#: cmdline/apt-get.cc:2383 +msgid "Supported modules:" +msgstr "Mô-đun đã hỗ trợ :" + +#: cmdline/apt-get.cc:2424 cmdline/apt-get.cc:2306 +msgid "" +"Usage: apt-get [options] command\n" +" apt-get [options] install|remove pkg1 [pkg2 ...]\n" +" apt-get [options] source pkg1 [pkg2 ...]\n" +"\n" +"apt-get is a simple command line interface for downloading and\n" +"installing packages. The most frequently used commands are update\n" +"and install.\n" +"\n" +"Commands:\n" +" update - Retrieve new lists of packages\n" +" upgrade - Perform an upgrade\n" +" install - Install new packages (pkg is libc6 not libc6.deb)\n" +" remove - Remove packages\n" +" source - Download source archives\n" +" build-dep - Configure build-dependencies for source packages\n" +" dist-upgrade - Distribution upgrade, see apt-get(8)\n" +" dselect-upgrade - Follow dselect selections\n" +" clean - Erase downloaded archive files\n" +" autoclean - Erase old downloaded archive files\n" +" check - Verify that there are no broken dependencies\n" +"\n" +"Options:\n" +" -h This help text.\n" +" -q Loggable output - no progress indicator\n" +" -qq No output except for errors\n" +" -d Download only - do NOT install or unpack archives\n" +" -s No-act. Perform ordering simulation\n" +" -y Assume Yes to all queries and do not prompt\n" +" -f Attempt to continue if the integrity check fails\n" +" -m Attempt to continue if archives are unlocatable\n" +" -u Show a list of upgraded packages as well\n" +" -b Build the source package after fetching it\n" +" -V Show verbose version numbers\n" +" -c=? Read this configuration file\n" +" -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" +"See the apt-get(8), sources.list(5) and apt.conf(5) manual\n" +"pages for more information and options.\n" +" This APT has Super Cow Powers.\n" +msgstr "" +"Cách sử dụng: apt-get [tùy_chọn...] lệnh\n" +" apt-get [tùy_chọn...] install|remove gói1 [gói2 ...]\n" +" apt-get [tùy_chọn...] source gói1 [gói2 ...]\n" +"\n" +"[get: \tlấy\n" +"install: \tcài đặt\n" +"remove: \tgỡ bỏ\n" +"source: \tnguồn]\n" +"\n" +"apt-get là một giao diện dòng lệnh đơn giản để tải về và cài đặt gói.\n" +"Những lệnh đã dùng thường nhất là update (cập nhật) và install (cài đặt).\n" +"\n" +"Lệnh:\n" +" update\t\tLấy danh sách gói mới (_cập nhật_)\n" +" upgrade \t_Nâng cập_ \n" +" install \t\t_Cài đặt_ gói mới (gói là libc6 không phải libc6.deb)\n" +" remove \t_Gỡ bỏ_ gói\n" +" source \t\tTải về kho _nguồn_\n" +" build-dep \tĐịnh cấu hình _cách phụ thuộc khi xây dụng_, cho gói nguồn\n" +" dist-upgrade \t_Nâng cấp bản phân phối_,\n" +"\t\t\t\t\thãy xem trang hướng dẫn (man) apt-get(8)\n" +" dselect-upgrade \t\tTheo cách chọn dselect (_nâng cấp_)\n" +" clean \t\tXoá bỏ các tập tin kho đã tải về (_làm sạch_)\n" +" autoclean \tXoá bỏ các tập tin kho cũ đã tải về (_tự động làm sạch_)\n" +" check \t\t_Kiểm chứng_ không có cách phụ thuộc bị ngắt\n" +"\n" +"Tùy chọn:\n" +" -h \t_Trợ giúp_ này.\n" +" -q \tDữ liệu xuất có thể ghi lưu - không có cái chỉ tiến trình (_im_)\n" +" -qq \tKhông xuất thông tin nào, trừ lỗi (_im im_)\n" +" -d \tChỉ _tải về_, ĐỪNG cài đặt hay giải nén kho\n" +" -s \tKhông hoạt đông. _Mô phỏng_ sắp xếp\n" +" -y \tGiả sử trả lời _Có_ (yes) mọi khi gặp câu hỏi;\n" +"\t\t\t\t\tđừng nhắc người dùng gõ gì\n" +" -f \t\tCố tiếp tục lại nếu việc kiểm tra tính nguyên vẹn _thất bại_\n" +" -m \tCố tiếp tục lại nếu không thể định vị kho\n" +" -u \tCũng hiện danh sách các gói đã _nâng cấp_\n" +" -b \t_Xây dụng_ gói nguồn sau khi lấy nó\n" +" -V \tHiện số thứ tự _phiên bản chi tiết_\n" +" -c=? \tĐọc tập tin cấu hình ấy\n" +" -o=? \tLập tùy chọn nhiệm ý, v.d. -o dir::cache=/tmp\n" +"Để tim thông tin và tùy chọn thêm thì hãy xem trang hướng dẫn apt-get(8), " +"sources.list(5) và apt.conf(5).\n" +" Trình APT này có năng lực của bò siêu.\n" + +#: cmdline/acqprogress.cc:55 src/download_bar.cc:76 +#: src/generic/acqprogress.cc:63 src/download_bar.cc:75 +msgid "Hit " +msgstr "Lần tìm " + +#: cmdline/acqprogress.cc:79 src/generic/acqprogress.cc:87 +msgid "Get:" +msgstr "Lấy:" + +#: cmdline/acqprogress.cc:110 src/generic/acqprogress.cc:118 +msgid "Ign " +msgstr "Bỏq " + +#: cmdline/acqprogress.cc:114 src/generic/acqprogress.cc:122 +msgid "Err " +msgstr "Lỗi " + +#: cmdline/acqprogress.cc:135 src/generic/acqprogress.cc:142 +#, c-format +msgid "Fetched %sB in %s (%sB/s)\n" +msgstr "Mới lấy %sB trong %s (%sB/g).\n" + +#: src/download_item.cc:70 src/download_item.cc:74 src/download_item.cc:104 +#, c-format +msgid " [Working]" +msgstr " [Hoạt động]" + +#: cmdline/acqprogress.cc:271 +#, c-format +msgid "" +"Media change: please insert the disc labeled\n" +" '%s'\n" +"in the drive '%s' and press enter\n" +msgstr "" +"Chuyển đổi vật chứa: hãy nạp đĩa có nhãn\n" +" « %s »\n" +"vào ổ « %s » và bấm nút Enter\n" + +#: cmdline/apt-sortpkgs.cc:86 +msgid "Unknown package record!" +msgstr "Không biết mục ghi gói." + +#: cmdline/apt-sortpkgs.cc:150 +msgid "" +"Usage: apt-sortpkgs [options] file1 [file2 ...]\n" +"\n" +"apt-sortpkgs is a simple tool to sort package files. The -s option is used\n" +"to indicate what kind of file it is.\n" +"\n" +"Options:\n" +" -h This help text\n" +" -s Use source file sorting\n" +" -c=? Read this configuration file\n" +" -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" +msgstr "" +"Cách sử dụng: apt-sortpkgs [tùy_chọn...] tập_tin1 [tập_tin2 ...]\n" +"\n" +"[sortpkgs: sort packages: sắp xếp các gói]\n" +"\n" +"apt-sortpkgs là một công cụ đơn giản để sắp xếp tập tin gói.\n" +"Tùy chon « -s » dùng để ngụ ý kiểu tập tin.\n" +"\n" +"Tùy chọn:\n" +" -h \t_Trợ giúp_ này\n" +" -s \tSắp xếp những tập tin _nguồn_\n" +" -c=? \tĐọc tập tin cấu hình này\n" +" -o=? \tLập tùy chọn cấu hình nhiệm ý, v.d. « -o dir::cache=/tmp »\n" + +#: dselect/install:32 +msgid "Bad default setting!" +msgstr "Thiết lập mặc định sai." + +#: dselect/install:51 dselect/install:83 dselect/install:87 dselect/install:93 +#: dselect/install:104 dselect/update:45 +msgid "Press enter to continue." +msgstr "Hãy bấm phím Enter để tiếp tục lại." + +#: dselect/install:100 +msgid "Some errors occurred while unpacking. I'm going to configure the" +msgstr "Gập một số lỗi khi giải nén. Sẽ cấu hình" + +#: dselect/install:101 +msgid "packages that were installed. This may result in duplicate errors" +msgstr "những gói đã đươc cài đặt. Có lẽ sẽ gây ra lỗi trùng" + +#: dselect/install:102 +msgid "or errors caused by missing dependencies. This is OK, only the errors" +msgstr "" +"hoặc lỗi khi không có phần mềm mà gói khác phụ thuộc vào nó. Không có sao, " +"chỉ những lỗi" + +#: dselect/install:103 +msgid "" +"above this message are important. Please fix them and run [I]nstall again" +msgstr "" +"ở trên thông điệp này là quan trọng. Hãy sửa chúng và chạy lại [I]nstall " +"(cài đặt)." + +#: dselect/update:30 +msgid "Merging available information" +msgstr "Đang hợp nhất các thông tin sẵn sàng..." + +#: apt-inst/contrib/extracttar.cc:117 apt-inst/contrib/extracttar.cc:116 +msgid "Failed to create pipes" +msgstr "Việc tạo những ống bị lỗi" + +#: apt-inst/contrib/extracttar.cc:143 apt-inst/contrib/extracttar.cc:141 +msgid "Failed to exec gzip " +msgstr "Việc thực hiện gzip bị lỗi " + +#: apt-inst/contrib/extracttar.cc:180 apt-inst/contrib/extracttar.cc:206 +#: apt-inst/contrib/extracttar.cc:178 apt-inst/contrib/extracttar.cc:204 +msgid "Corrupted archive" +msgstr "Kho bị hỏng." + +#: apt-inst/contrib/extracttar.cc:195 +msgid "Tar checksum failed, archive corrupted" +msgstr "Tiến trình tar (kiểm tổng tar) thât bại: kho bị hỏng." + +#: apt-inst/contrib/extracttar.cc:298 apt-inst/contrib/extracttar.cc:296 +#, c-format +msgid "Unknown TAR header type %u, member %s" +msgstr "Không biết kiểu phần đầu tar %u, bộ phạn %s" + +#: apt-inst/contrib/arfile.cc:73 +msgid "Invalid archive signature" +msgstr "Chữ ký kho không hợp lệ" + +#: apt-inst/contrib/arfile.cc:81 +msgid "Error reading archive member header" +msgstr "Gặp lỗi khi đọc phần đầu bộ phạn kho" + +#: apt-inst/contrib/arfile.cc:93 apt-inst/contrib/arfile.cc:105 +msgid "Invalid archive member header" +msgstr "Phần đầu bộ phạn kho không hợp lê" + +#: apt-inst/contrib/arfile.cc:131 +msgid "Archive is too short" +msgstr "Kho quá ngắn" + +#: apt-inst/contrib/arfile.cc:135 +msgid "Failed to read the archive headers" +msgstr "Việc đọc phần đầu kho bị lỗi" + +#: apt-inst/filelist.cc:384 +msgid "DropNode called on still linked node" +msgstr "DropNode (thả điểm nút) được gọi với điểm nút còn liên kết" + +#: apt-inst/filelist.cc:416 +msgid "Failed to locate the hash element!" +msgstr "Việc định vi phần tử băm bị lỗi." + +#: apt-inst/filelist.cc:463 +msgid "Failed to allocate diversion" +msgstr "Việc cấp phát sự trệch đi bị lỗi" + +#: apt-inst/filelist.cc:468 +msgid "Internal error in AddDiversion" +msgstr "Lỗi nội bộ trong AddDiversion (thêm sự trệch đi)" + +#: apt-inst/filelist.cc:481 +#, c-format +msgid "Trying to overwrite a diversion, %s -> %s and %s/%s" +msgstr "Đang cố ghi đè một sự trệch đi, %s → %s và %s/%s" + +#: apt-inst/filelist.cc:510 +#, c-format +msgid "Double add of diversion %s -> %s" +msgstr "Sự trệch đi được thêm hai lần %s → %s" + +#: apt-inst/filelist.cc:553 +#, c-format +msgid "Duplicate conf file %s/%s" +msgstr "Tập tin cấu hình trùng %s/%s" + +#: apt-inst/dirstream.cc:45 apt-inst/dirstream.cc:50 apt-inst/dirstream.cc:53 +#, c-format +msgid "Failed to write file %s" +msgstr "Việc ghi tập tin %s bị lỗi" + +#: apt-inst/dirstream.cc:96 apt-inst/dirstream.cc:104 apt-inst/dirstream.cc:80 +#: apt-inst/dirstream.cc:88 +#, c-format +msgid "Failed to close file %s" +msgstr "Việc đóng tập tin %s bị lỗi" + +#: apt-inst/extract.cc:96 apt-inst/extract.cc:167 +#, c-format +msgid "The path %s is too long" +msgstr "Đường dẫn %s quá dài" + +#: apt-inst/extract.cc:127 +#, c-format +msgid "Unpacking %s more than once" +msgstr "Đang giải nén %s nhiều lần" + +#: apt-inst/extract.cc:137 +#, c-format +msgid "The directory %s is diverted" +msgstr "Thư mục %s bị trệch hướng" + +#: apt-inst/extract.cc:147 +#, c-format +msgid "The package is trying to write to the diversion target %s/%s" +msgstr "Gói này đang cố ghi vào đích trệch đi %s/%s" + +#: apt-inst/extract.cc:157 apt-inst/extract.cc:300 +msgid "The diversion path is too long" +msgstr "Đường dẫn trệch đi quá dài." + +#: apt-inst/extract.cc:243 +#, c-format +msgid "The directory %s is being replaced by a non-directory" +msgstr "Thư mục %s đang được thay thế do điều không phải là thư mục" + +#: apt-inst/extract.cc:283 +msgid "Failed to locate node in its hash bucket" +msgstr "Việc định vị điểm nút trong hộp băm nó bị lỗi" + +#: apt-inst/extract.cc:287 +msgid "The path is too long" +msgstr "Đường dẫn quá dài" + +#: apt-inst/extract.cc:417 +#, c-format +msgid "Overwrite package match with no version for %s" +msgstr "Ghi đè lên gói đã khớp mà không có phiên bản cho %s" + +#: apt-inst/extract.cc:434 +#, c-format +msgid "File %s/%s overwrites the one in the package %s" +msgstr "Tập tin %s/%s ghi đè lên điều trong gói %s" + +#: apt-pkg/contrib/configuration.cc:709 apt-pkg/acquire.cc:416 +#, c-format +msgid "Unable to read %s" +msgstr "Không thể đọc %s" + +#: apt-inst/extract.cc:494 +#, c-format +msgid "Unable to stat %s" +msgstr "Không thể lấy các thông tin về %s" + +#: apt-inst/deb/dpkgdb.cc:55 apt-inst/deb/dpkgdb.cc:61 +#, c-format +msgid "Failed to remove %s" +msgstr "Việc gỡ bỏ %s bị lỗi" + +#: apt-inst/deb/dpkgdb.cc:110 apt-inst/deb/dpkgdb.cc:112 +#, c-format +msgid "Unable to create %s" +msgstr "Không thể tạo %s" + +#: apt-inst/deb/dpkgdb.cc:118 +#, c-format +msgid "Failed to stat %sinfo" +msgstr "Việc lấy các thông tin về %sinfo bị lỗi" + +#: apt-inst/deb/dpkgdb.cc:123 +msgid "The info and temp directories need to be on the same filesystem" +msgstr "" +"Những thư mục info (thông tin) và temp (tạm thời) cần phải trong cùng một hệ " +"thống tập tin" + +#: apt-pkg/pkgcachegen.cc:840 +msgid "Reading package lists" +msgstr "Đang đọc các danh sách gói..." + +#: apt-inst/deb/dpkgdb.cc:180 +#, c-format +msgid "Failed to change to the admin dir %sinfo" +msgstr "Việc chuyển đổi sang thư mục quản lý %sinfo bị lỗi" + +#: apt-inst/deb/dpkgdb.cc:448 +msgid "Internal error getting a package name" +msgstr "Gặp lỗi nội bộ khi lấy tên gói" + +#: apt-inst/deb/dpkgdb.cc:205 +msgid "Reading file listing" +msgstr "Đang đọc danh sách tập tin..." + +#: apt-inst/deb/dpkgdb.cc:216 +#, c-format +msgid "" +"Failed to open the list file '%sinfo/%s'. If you cannot restore this file " +"then make it empty and immediately re-install the same version of the " +"package!" +msgstr "" +"Việc mở tập tin danh sách « %sinfo/%s » bị lỗi. Nếu bạn không thể phục hồi " +"tập tin này, bạn hãy làm cho nó rỗng và ngay cài đặt lại cùng phiên bản gói." + +#: apt-inst/deb/dpkgdb.cc:229 apt-inst/deb/dpkgdb.cc:242 +#, c-format +msgid "Failed reading the list file %sinfo/%s" +msgstr "Việc đọc tập tin danh sách %sinfo/%s bị lỗi" + +#: apt-inst/deb/dpkgdb.cc:266 +msgid "Internal error getting a node" +msgstr "Gặp lỗi nội bộ khi lấy nút điểm..." + +#: apt-inst/deb/dpkgdb.cc:309 +#, c-format +msgid "Failed to open the diversions file %sdiversions" +msgstr "Việc mở tập tin trệch đi %sdiversions bị lỗi" + +#: apt-inst/deb/dpkgdb.cc:324 +msgid "The diversion file is corrupted" +msgstr "Tập tin trệch đi bị hỏng" + +#: apt-inst/deb/dpkgdb.cc:331 apt-inst/deb/dpkgdb.cc:336 +#: apt-inst/deb/dpkgdb.cc:341 +#, c-format +msgid "Invalid line in the diversion file: %s" +msgstr "Gặp dòng không hợp lệ trong tập tin trệch đi: %s" + +#: apt-inst/deb/dpkgdb.cc:362 +msgid "Internal error adding a diversion" +msgstr "Gặp lỗi nội bộ khi thêm một sự trệch đi" + +#: apt-inst/deb/dpkgdb.cc:383 +msgid "The pkg cache must be initialized first" +msgstr "Phải khởi động bộ nhớ tạm gói trước hết" + +#: apt-inst/deb/dpkgdb.cc:386 +msgid "Reading file list" +msgstr "Đang đọc danh sách tâp tin..." + +#: apt-inst/deb/dpkgdb.cc:443 +#, c-format +msgid "Failed to find a Package: header, offset %lu" +msgstr "Lỗi tìm thấy Gói: phần đầu, hiệu số %lu" + +#: apt-inst/deb/dpkgdb.cc:465 +#, c-format +msgid "Bad ConfFile section in the status file. Offset %lu" +msgstr "" +"Có phần cấu hình tập tin (ConfFile) sai trong tập tin trạng thái. Hiệu số %lu" + +#: apt-inst/deb/dpkgdb.cc:470 +#, c-format +msgid "Error parsing MD5. Offset %lu" +msgstr "Gặp lỗi khi phân tách MD5. Hiệu số %lu" + +#: apt-inst/deb/debfile.cc:42 apt-inst/deb/debfile.cc:47 +#: apt-inst/deb/debfile.cc:55 +#, c-format +msgid "This is not a valid DEB archive, missing '%s' member" +msgstr "Đây không phải là môt kho DEB hợp lệ vì còn thiếu bộ phạn « %s »" + +#: apt-inst/deb/debfile.cc:52 +#, c-format +msgid "This is not a valid DEB archive, it has no '%s' or '%s' member" +msgstr "" +"Đây không phải là môt kho DEB hợp lệ vì không có bộ phạn « %s » hay « %s »" + +#: apt-inst/deb/debfile.cc:112 apt-inst/deb/debfile.cc:104 +#, c-format +msgid "Couldn't change to %s" +msgstr "Không thể chuyển đổi sang %s" + +#: apt-inst/deb/debfile.cc:138 +msgid "Internal error, could not locate member" +msgstr "Gặp lỗi nội bộ, không thể định vị bộ phạn" + +#: apt-inst/deb/debfile.cc:171 apt-inst/deb/debfile.cc:158 +msgid "Failed to locate a valid control file" +msgstr "Việc định vị tập tin điều khiển hợp lệ bị lỗi" + +#: apt-inst/deb/debfile.cc:256 +msgid "Unparsable control file" +msgstr "Tập tin điều khiển không có khả năng phân tách" + +#: methods/cdrom.cc:114 methods/cdrom.cc:113 +#, c-format +msgid "Unable to read the cdrom database %s" +msgstr "Không thể đọc cơ sở dữ liệu đĩa CD-ROM %s" + +#: methods/cdrom.cc:123 +msgid "" +"Please use apt-cdrom to make this CD-ROM recognized by APT. apt-get update " +"cannot be used to add new CD-ROMs" +msgstr "" +"Hãy sử dụng lệnh « apt-cdrom » để làm cho APT chấp nhận đĩa CD này. Không " +"thể sử dụng lệnh « apt-get update » (lấy cập nhật) để thêm đĩa CD mới." + +#: methods/cdrom.cc:131 +msgid "Wrong CD-ROM" +msgstr "CD không đúng" + +#: methods/cdrom.cc:164 methods/cdrom.cc:163 +#, c-format +msgid "Unable to unmount the CD-ROM in %s, it may still be in use." +msgstr "Không thể tháo gắn kết đĩa CD-ROM trong %s. Có lẽ nó còn dùng." + +#: methods/cdrom.cc:169 +msgid "Disk not found." +msgstr "Không tìm thấy đĩa" + +#: methods/cdrom.cc:177 methods/file.cc:79 methods/rsh.cc:264 +#: ../libfspot/f-jpeg-utils.c:445 +msgid "File not found" +msgstr "Không tìm thấy tập tin" + +#: methods/copy.cc:42 methods/gpgv.cc:269 methods/gzip.cc:133 +#: methods/gzip.cc:142 +msgid "Failed to stat" +msgstr "Việc lấy các thông tin bị lỗi" + +#: methods/copy.cc:79 methods/gpgv.cc:266 methods/gzip.cc:139 +msgid "Failed to set modification time" +msgstr "Việc lập giờ sửa đổi bị lỗi" + +#: methods/file.cc:44 methods/file.cc:42 +msgid "Invalid URI, local URIS must not start with //" +msgstr "Địa chỉ Mạng (URI) không hợp lệ: URI không thể bắt đầu với « // »" + +#: methods/ftp.cc:162 +msgid "Logging in" +msgstr "Đang đăng nhập..." + +#: methods/ftp.cc:168 +msgid "Unable to determine the peer name" +msgstr "Không thể quyết định tên ngang hàng" + +#: methods/ftp.cc:173 +msgid "Unable to determine the local name" +msgstr "Không thể quyết định tên cục bộ" + +#: methods/ftp.cc:204 methods/ftp.cc:232 +#, c-format +msgid "The server refused the connection and said: %s" +msgstr "Máy phục vụ đã từ chối kết nối, và nói: %s" + +#: methods/ftp.cc:210 +#, c-format +msgid "USER failed, server said: %s" +msgstr "Lệnh USER (người dùng) đã thất bại: máy phục vụ nói: %s" + +#: methods/ftp.cc:217 +#, c-format +msgid "PASS failed, server said: %s" +msgstr "Lệnh PASS (mật khẩu) đã thất bại: máy phục vụ nói: %s" + +#: methods/ftp.cc:237 +msgid "" +"A proxy server was specified but no login script, Acquire::ftp::ProxyLogin " +"is empty." +msgstr "" +"Đã ghi rõ máy phục vụ ủy nhiệm, nhưng mà chưa ghi rõ tập lệnh đăng nhập. « " +"Acquire::ftp::ProxyLogin » là rỗng." + +#: methods/ftp.cc:265 +#, c-format +msgid "Login script command '%s' failed, server said: %s" +msgstr "Lệnh tập lệnh đăng nhập « %s » đã thất bại: máy phục vụ nói: %s" + +#: methods/ftp.cc:291 +#, c-format +msgid "TYPE failed, server said: %s" +msgstr "Lệnh TYPE (kiểu) đã thất bại: máy phục vụ nói: %s" + +#: methods/ftp.cc:329 methods/ftp.cc:440 methods/rsh.cc:183 methods/rsh.cc:226 +msgid "Connection timeout" +msgstr "Thời hạn kết nối" + +#: methods/ftp.cc:335 +msgid "Server closed the connection" +msgstr "Máy phục vụ đã đóng kết nối" + +#: src/uuencode.c:190 +msgid "Read error" +msgstr "Lỗi đọc" + +#: methods/ftp.cc:345 methods/rsh.cc:197 +msgid "A response overflowed the buffer." +msgstr "Một trả lời đã tràn bộ đệm." + +#: methods/ftp.cc:362 methods/ftp.cc:374 +msgid "Protocol corruption" +msgstr "Giao thức bị hỏng" + +#: src/uuencode.c:310 +msgid "Write error" +msgstr "Lỗi ghi" + +#: methods/ftp.cc:687 methods/ftp.cc:693 methods/ftp.cc:729 +msgid "Could not create a socket" +msgstr "Không thể tạo ổ cắm" + +#: methods/ftp.cc:698 +msgid "Could not connect data socket, connection timed out" +msgstr "Không thể kết nối ổ cắm dữ liệu, kết nối đã quá giờ" + +#: methods/ftp.cc:704 +msgid "Could not connect passive socket." +msgstr "Không thể kết nối ổ cắm bị động." + +#: methods/ftp.cc:722 +msgid "getaddrinfo was unable to get a listening socket" +msgstr "getaddrinfo (lấy thông tin địa chỉ) không thể lấy ổ cắm lắng nghe" + +#: methods/ftp.cc:736 +msgid "Could not bind a socket" +msgstr "Không thể đóng kết ổ cắm" + +#: methods/ftp.cc:740 +msgid "Could not listen on the socket" +msgstr "Không thể lắng nghe trên ổ cắm đó" + +#: methods/ftp.cc:747 +msgid "Could not determine the socket's name" +msgstr "Không thể quyết định tên ổ cắm đó" + +#: methods/ftp.cc:779 +msgid "Unable to send PORT command" +msgstr "Không thể gởi lệnh PORT (cổng)" + +#: methods/ftp.cc:789 +#, c-format +msgid "Unknown address family %u (AF_*)" +msgstr "Không biết nhóm địa chỉ %u (AF_*)" + +#: methods/ftp.cc:798 +#, c-format +msgid "EPRT failed, server said: %s" +msgstr "Lệnh EPRT (thông báo lỗi) đã thất bại: máy phục vụ nói: %s" + +#: methods/ftp.cc:818 +msgid "Data socket connect timed out" +msgstr "Kết nối ổ cắm dữ liệu đã quá giờ" + +#: methods/ftp.cc:825 +msgid "Unable to accept connection" +msgstr "Không thể chấp nhận kết nối" + +#: methods/ftp.cc:864 methods/http.cc:958 methods/rsh.cc:303 +#: methods/http.cc:916 +msgid "Problem hashing file" +msgstr "Gặp khó khăn băm tập tin" + +#: methods/ftp.cc:877 +#, c-format +msgid "Unable to fetch file, server said '%s'" +msgstr "Không thể lấy tập tin: máy phục vụ nói « %s »" + +#: methods/ftp.cc:892 methods/rsh.cc:322 +msgid "Data socket timed out" +msgstr "Ổ cắm dữ liệu đã quá giờ" + +#: methods/ftp.cc:922 +#, c-format +msgid "Data transfer failed, server said '%s'" +msgstr "Việc truyền dữ liệu bị lỗi: máy phục vụ nói « %s »" + +#: ../app/dialogs/module-dialog.c:477 ../objects/UML/class_dialog.c:2050 +#: ../glade/search.glade.h:6 +msgid "Query" +msgstr "Truy vấn" + +#: methods/ftp.cc:1106 +msgid "Unable to invoke " +msgstr "Không thể gọi " + +#: methods/connect.cc:64 +#, c-format +msgid "Connecting to %s (%s)" +msgstr "Đang kết nối đến %s (%s)..." + +#: methods/connect.cc:71 +#, c-format +msgid "[IP: %s %s]" +msgstr "[Địa chỉ IP: %s %s]" + +#: methods/connect.cc:80 +#, c-format +msgid "Could not create a socket for %s (f=%u t=%u p=%u)" +msgstr "Không thể tạo ổ cắm cho %s (f=%u t=%u p=%u)" + +#: methods/connect.cc:86 +#, c-format +msgid "Cannot initiate the connection to %s:%s (%s)." +msgstr "Không thể sở khởi kết nối đến %s:%s (%s)." + +#: methods/connect.cc:93 methods/connect.cc:92 +#, c-format +msgid "Could not connect to %s:%s (%s), connection timed out" +msgstr "Không thể kết nối đến %s:%s (%s), kết nối đã quá giờ" + +#: methods/connect.cc:106 methods/connect.cc:104 +#, c-format +msgid "Could not connect to %s:%s (%s)." +msgstr "Không thể kết nối đến %s:%s (%s)." + +#: methods/connect.cc:134 methods/rsh.cc:425 methods/connect.cc:132 +#: src/gpsdrive.c:7398 src/gpsdrive.c:7400 src/gpsdrive.c:7402 +#, c-format +msgid "Connecting to %s" +msgstr "Đang kết nối đến %s..." + +#: methods/connect.cc:165 methods/connect.cc:163 +#, c-format +msgid "Could not resolve '%s'" +msgstr "Không thể tháo gỡ « %s »" + +#: methods/connect.cc:171 methods/connect.cc:167 +#, c-format +msgid "Temporary failure resolving '%s'" +msgstr "Việc tháo gỡ « %s » bị lỗi tạm thời" + +#: methods/connect.cc:174 methods/connect.cc:169 +#, c-format +msgid "Something wicked happened resolving '%s:%s' (%i)" +msgstr "Gặp lỗi nghiệm trọng khi tháo gỡ « %s:%s » (%i)" + +#: methods/connect.cc:221 methods/connect.cc:216 +#, c-format +msgid "Unable to connect to %s %s:" +msgstr "Không thể kết nối đến %s %s:" + +#: methods/gpgv.cc:92 +msgid "E: Argument list from Acquire::gpgv::Options too long. Exiting." +msgstr "E: Danh sách lệnh từ « Acquire::gpgv::Options » quá dài nên thoát." + +#: methods/gpgv.cc:191 +msgid "" +"Internal error: Good signature, but could not determine key fingerprint?!" +msgstr "Lỗi nội bộ: chữ ký đúng, nhưng không thể quyết định vân tay khoá ?!" + +#: methods/gpgv.cc:196 +msgid "At least one invalid signature was encountered." +msgstr "Gặp ít nhất một chữ ký không hợp lệ." + +#. FIXME String concatenation considered harmful. +#: methods/gpgv.cc:201 +msgid "Could not execute " +msgstr "Không thể thực hiện " + +#: methods/gpgv.cc:202 +msgid " to verify signature (is gnupg installed?)" +msgstr " để kiểm chứng chữ ký (gnupg có được cài đặt chưa?)" + +#: methods/gpgv.cc:206 +msgid "Unknown error executing gpgv" +msgstr "Gặp lỗi lạ khi thực hiện gpgv" + +#: methods/gpgv.cc:237 +msgid "The following signatures were invalid:\n" +msgstr "Những chữ ký theo đây là không hợp lệ:\n" + +#: methods/gpgv.cc:244 +msgid "" +"The following signatures couldn't be verified because the public key is not " +"available:\n" +msgstr "" +"Không thể kiểm chứng những chữ ký theo đây, vì khoá công không sẵn sàng:\n" + +#: methods/gzip.cc:57 +#, c-format +msgid "Couldn't open pipe for %s" +msgstr "Không thể mở ống dẫn cho %s" + +#: methods/gzip.cc:102 +#, c-format +msgid "Read error from %s process" +msgstr "Gặp lỗi đọc từ tiến trình %s" + +#: methods/http.cc:376 methods/http.cc:344 +msgid "Waiting for headers" +msgstr "Đang đợi những phần đầu" + +#: methods/http.cc:522 methods/http.cc:490 +#, c-format +msgid "Got a single header line over %u chars" +msgstr "Đã lấy một dòng đầu riêng lẻ chứa hơn %u ky tự" + +#: methods/http.cc:530 methods/http.cc:498 +msgid "Bad header line" +msgstr "Dòng đầu sai" + +#: methods/http.cc:549 methods/http.cc:556 +msgid "The HTTP server sent an invalid reply header" +msgstr "Máy phục vụ HTTP đã gởi một dòng đầu trả lời không hợp lệ" + +#: methods/http.cc:585 +msgid "The HTTP server sent an invalid Content-Length header" +msgstr "" +"Máy phục vụ HTTP đã gởi một dòng đầu Content-Length (độ dài nội dụng) không " +"hợp lệ" + +#: methods/http.cc:600 +msgid "The HTTP server sent an invalid Content-Range header" +msgstr "" +"Máy phục vụ HTTP đã gởi một dòng đầu Content-Range (phạm vị nội dụng) không " +"hợp lệ" + +#: methods/http.cc:602 +msgid "This HTTP server has broken range support" +msgstr "Máy phục vụ HTTP đã ngắt cách hỗ trợ phạm vị" + +#: methods/http.cc:626 methods/http.cc:594 +msgid "Unknown date format" +msgstr "Không biết dạng ngày đó" + +#: methods/http.cc:773 methods/http.cc:737 +msgid "Select failed" +msgstr "Việc chọn bị lỗi" + +#: methods/http.cc:778 src/common/util.c:306 methods/http.cc:742 +msgid "Connection timed out" +msgstr "Kết nối đã quá giờ" + +#: methods/http.cc:801 methods/http.cc:765 +msgid "Error writing to output file" +msgstr "Gặp lỗi khi ghi vào tập tin xuất" + +#: methods/http.cc:832 methods/http.cc:793 +msgid "Error writing to file" +msgstr "Gặp lỗi khi ghi vào tập tin" + +#: methods/http.cc:860 methods/http.cc:818 +msgid "Error writing to the file" +msgstr "Gặp lỗi khi ghi vào tập tin đó" + +#: methods/http.cc:874 +msgid "Error reading from server. Remote end closed connection" +msgstr "Gặp lỗi khi đọc từ máy phục vụ : cuối ở xa đã đóng kết nối" + +#: methods/http.cc:876 methods/http.cc:834 +msgid "Error reading from server" +msgstr "Gặp lỗi khi đọc từ máy phục vụ" + +#: methods/http.cc:1107 +msgid "Bad header data" +msgstr "Dữ liệu dòng đầu sai" + +#: methods/http.cc:1124 ../libgames-support/games-network.c:357 +#: methods/http.cc:1082 +msgid "Connection failed" +msgstr "Kết nối bị ngắt" + +#: src/err-codes.h:91 ../libmuine/player-xine.c:398 ../glom/base_db.cc:78 +#: ../glom/connectionpool.cc:348 libexif/olympus/mnote-olympus-entry.c:314 +msgid "Internal error" +msgstr "Lỗi nội bộ" + +#: apt-pkg/contrib/mmap.cc:82 +msgid "Can't mmap an empty file" +msgstr "Không thể mmap (ảnh xạ bộ nhớ) tâp tin rỗng" + +#: apt-pkg/contrib/mmap.cc:87 +#, c-format +msgid "Couldn't make mmap of %lu bytes" +msgstr "Không thể tạo mmap (ảnh xạ bộ nhớ) kích cỡ %lu byte" + +#: apt-pkg/contrib/strutl.cc:938 apt-pkg/contrib/strutl.cc:941 +#, c-format +msgid "Selection %s not found" +msgstr "Không tìm thấy vùng chọn %s" + +#: apt-pkg/contrib/configuration.cc:436 apt-pkg/contrib/configuration.cc:395 +#, c-format +msgid "Unrecognized type abbreviation: '%c'" +msgstr "Không nhận biết viết tắt kiểu : « %c »" + +#: apt-pkg/contrib/configuration.cc:494 apt-pkg/contrib/configuration.cc:453 +#, c-format +msgid "Opening configuration file %s" +msgstr "Đang mở tập tin cấu hình %s..." + +#: apt-pkg/contrib/configuration.cc:512 apt-pkg/contrib/configuration.cc:471 +#, c-format +msgid "Line %d too long (max %d)" +msgstr "Dòng %d quá dài (tối đa %d)" + +#: apt-pkg/contrib/configuration.cc:608 apt-pkg/contrib/configuration.cc:567 +#, c-format +msgid "Syntax error %s:%u: Block starts with no name." +msgstr "Gặp lỗi cú pháp %s:%u: khối bắt đầu không có tên." + +#: apt-pkg/contrib/configuration.cc:627 +#, c-format +msgid "Syntax error %s:%u: Malformed tag" +msgstr "Gặp lỗi cú pháp %s:%u: thẻ dạng sai" + +#: apt-pkg/contrib/configuration.cc:644 apt-pkg/contrib/configuration.cc:603 +#, c-format +msgid "Syntax error %s:%u: Extra junk after value" +msgstr "Gặp lỗi cú pháp %s:%u: có rác thêm sau giá trị" + +#: apt-pkg/contrib/configuration.cc:684 apt-pkg/contrib/configuration.cc:643 +#, c-format +msgid "Syntax error %s:%u: Directives can only be done at the top level" +msgstr "Gặp lỗi cú pháp %s:%u: có thể thực hiện chỉ thị chỉ tại mức đầu" + +#: apt-pkg/contrib/configuration.cc:691 apt-pkg/contrib/configuration.cc:650 +#, c-format +msgid "Syntax error %s:%u: Too many nested includes" +msgstr "Gặp lỗi cú pháp %s:%u: quá nhiều điều bao gồm lồng nhau" + +#: apt-pkg/contrib/configuration.cc:695 apt-pkg/contrib/configuration.cc:700 +#: apt-pkg/contrib/configuration.cc:654 apt-pkg/contrib/configuration.cc:659 +#, c-format +msgid "Syntax error %s:%u: Included from here" +msgstr "Gặp lỗi cú pháp %s:%u: đã bao gồm từ đây" + +#: apt-pkg/contrib/configuration.cc:704 apt-pkg/contrib/configuration.cc:663 +#, c-format +msgid "Syntax error %s:%u: Unsupported directive '%s'" +msgstr "Gặp lỗi cú pháp %s:%u: chưa hỗ trợ chỉ thị « %s »" + +#: apt-pkg/contrib/configuration.cc:738 apt-pkg/contrib/configuration.cc:697 +#, c-format +msgid "Syntax error %s:%u: Extra junk at end of file" +msgstr "Gặp lỗi cú pháp %s:%u: rác thêm tại kết thúc tập tin" + +#: apt-pkg/contrib/progress.cc:154 +#, c-format +msgid "%c%s... Error!" +msgstr "%c%s... Lỗi." + +#: apt-pkg/contrib/progress.cc:156 +#, c-format +msgid "%c%s... Done" +msgstr "%c%s... Xong" + +#: apt-pkg/contrib/cmndline.cc:80 +#, c-format +msgid "Command line option '%c' [from %s] is not known." +msgstr "Không biết tùy chọn dòng lệnh « %c » [từ %s]." + +#: apt-pkg/contrib/cmndline.cc:106 apt-pkg/contrib/cmndline.cc:114 +#: apt-pkg/contrib/cmndline.cc:122 +#, c-format +msgid "Command line option %s is not understood" +msgstr "Không hiểu tùy chọn dòng lệnh %s" + +#: apt-pkg/contrib/cmndline.cc:127 +#, c-format +msgid "Command line option %s is not boolean" +msgstr "Tùy chọn dòng lệnh %s không phải bun (đúng/không đúng)" + +#: apt-pkg/contrib/cmndline.cc:166 apt-pkg/contrib/cmndline.cc:187 +#, c-format +msgid "Option %s requires an argument." +msgstr "Tùy chọn %s cần đến một đối số." + +#: apt-pkg/contrib/cmndline.cc:201 apt-pkg/contrib/cmndline.cc:207 +#, c-format +msgid "Option %s: Configuration item specification must have an =." +msgstr "Tùy chọn %s: đặc tả mục cấu hình phải có một « = »." + +#: apt-pkg/contrib/cmndline.cc:237 +#, c-format +msgid "Option %s requires an integer argument, not '%s'" +msgstr "Tùy chọn %s cần đến một đối số số nguyên, không phải « %s »" + +#: apt-pkg/contrib/cmndline.cc:268 +#, c-format +msgid "Option '%s' is too long" +msgstr "Tùy chọn « %s » quá dài" + +#: apt-pkg/contrib/cmndline.cc:301 +#, c-format +msgid "Sense %s is not understood, try true or false." +msgstr "Không hiểu %s: hãy cố dùng true (đúng) hay false (không đúng)." + +#: apt-pkg/contrib/cmndline.cc:351 +#, c-format +msgid "Invalid operation %s" +msgstr "Thao tác không hợp lệ %s" + +#: apt-pkg/contrib/cdromutl.cc:55 +#, c-format +msgid "Unable to stat the mount point %s" +msgstr "Không thể lấy các thông tin cho điểm gắn kết %s" + +#: apt-pkg/contrib/cdromutl.cc:149 apt-pkg/acquire.cc:427 apt-pkg/clean.cc:44 +#: apt-pkg/acquire.cc:422 +#, c-format +msgid "Unable to change to %s" +msgstr "Không thể chuyển đổi sang %s" + +#: apt-pkg/contrib/cdromutl.cc:190 +msgid "Failed to stat the cdrom" +msgstr "Việc lấy cac thông tin cho đĩa CD-ROM bị lỗi" + +#: apt-pkg/contrib/fileutl.cc:82 apt-pkg/contrib/fileutl.cc:80 +#, c-format +msgid "Not using locking for read only lock file %s" +msgstr "Không dùng khả năng khoá cho tập tin khoá chỉ đọc %s" + +#: apt-pkg/contrib/fileutl.cc:87 apt-pkg/contrib/fileutl.cc:85 +#, c-format +msgid "Could not open lock file %s" +msgstr "Không thể mở tập tin khoá %s" + +#: apt-pkg/contrib/fileutl.cc:105 apt-pkg/contrib/fileutl.cc:103 +#, c-format +msgid "Not using locking for nfs mounted lock file %s" +msgstr "Không dùng khả năng khoá cho tập tin khoá đã lắp kiểu NFS %s" + +#: apt-pkg/contrib/fileutl.cc:109 apt-pkg/contrib/fileutl.cc:107 +#, c-format +msgid "Could not get lock %s" +msgstr "Không thể lấy khoá %s" + +#: apt-pkg/contrib/fileutl.cc:377 +#, c-format +msgid "Waited for %s but it wasn't there" +msgstr "Đã đợi %s nhưng mà chưa gặp nó" + +#: apt-pkg/contrib/fileutl.cc:387 apt-pkg/contrib/fileutl.cc:368 +#, c-format +msgid "Sub-process %s received a segmentation fault." +msgstr "Tiến trình con %s đã nhận một lỗi chia ra từng đoạn." + +#: apt-pkg/contrib/fileutl.cc:390 apt-pkg/contrib/fileutl.cc:371 +#, c-format +msgid "Sub-process %s returned an error code (%u)" +msgstr "Tiến trình con %s đã trả lời mã lỗi (%u)" + +#: apt-pkg/contrib/fileutl.cc:392 apt-pkg/contrib/fileutl.cc:373 +#, c-format +msgid "Sub-process %s exited unexpectedly" +msgstr "Tiến trình con %s đã thoát bất ngờ" + +#: ../providers/xbase/gda-xbase-provider.c:246 +#, c-format +msgid "Could not open file %s" +msgstr "Không thể mở tập tin %s" + +#: apt-pkg/contrib/fileutl.cc:492 apt-pkg/contrib/fileutl.cc:473 +#, c-format +msgid "read, still have %lu to read but none left" +msgstr "đọc, còn cần đọc %lu nhưng mà không có điều còn lại" + +#: apt-pkg/contrib/fileutl.cc:522 apt-pkg/contrib/fileutl.cc:503 +#, c-format +msgid "write, still have %lu to write but couldn't" +msgstr "ghi, còn cần ghi %lu nhưng mà không thể" + +#: apt-pkg/contrib/fileutl.cc:597 apt-pkg/contrib/fileutl.cc:578 +msgid "Problem closing the file" +msgstr "Gặp lỗi khi đóng tập tin đó" + +#: apt-pkg/contrib/fileutl.cc:603 apt-pkg/contrib/fileutl.cc:584 +msgid "Problem unlinking the file" +msgstr "Gặp lỗi khi bỏ liên kết tập tin đó" + +#: apt-pkg/contrib/fileutl.cc:614 apt-pkg/contrib/fileutl.cc:595 +msgid "Problem syncing the file" +msgstr "Gặp lỗi khi đồng bộ hóa tập tin đó" + +#: apt-pkg/pkgcache.cc:126 +msgid "Empty package cache" +msgstr "Bộ nhớ tạm gói rỗng" + +#: apt-pkg/pkgcache.cc:132 +msgid "The package cache file is corrupted" +msgstr "Tập tin bộ nhớ tạm gói bị hỏng" + +#: apt-pkg/pkgcache.cc:137 +msgid "The package cache file is an incompatible version" +msgstr "Tập tin bộ nhớ tạm gói là một phiên bản không tương thích" + +#: apt-pkg/pkgcache.cc:142 +#, c-format +msgid "This APT does not support the versioning system '%s'" +msgstr "Trình APT này không hỗ trợ hệ thống điều khiển phiên bản « %s »" + +#: apt-pkg/pkgcache.cc:147 +msgid "The package cache was built for a different architecture" +msgstr "Bộ nhớ tạm gói được xây dụng cho kiến trức khác" + +#: apt-pkg/pkgcache.cc:218 src/cmdline/cmdline_show.cc:311 +#: src/cmdline/cmdline_show.cc:310 +msgid "Depends" +msgstr "Phụ thuộc" + +#: apt-pkg/pkgcache.cc:218 src/cmdline/cmdline_show.cc:313 +#: src/cmdline/cmdline_show.cc:312 +msgid "PreDepends" +msgstr "Phụ thuộc trước" + +#: apt-pkg/pkgcache.cc:218 src/cmdline/cmdline_show.cc:317 +#: src/cmdline/cmdline_show.cc:316 +msgid "Suggests" +msgstr "Đệ nghị" + +#: apt-pkg/pkgcache.cc:219 src/cmdline/cmdline_show.cc:315 +#: src/cmdline/cmdline_show.cc:314 +msgid "Recommends" +msgstr "Khuyên" + +#: apt-pkg/pkgcache.cc:219 ../objects/KAOS/metabinrel.c:157 +#: src/cmdline/cmdline_show.cc:319 src/cmdline/cmdline_show.cc:318 +msgid "Conflicts" +msgstr "Xung đột" + +#: apt-pkg/pkgcache.cc:219 src/cmdline/cmdline_show.cc:321 +#: src/cmdline/cmdline_show.cc:320 +msgid "Replaces" +msgstr "Thay thế" + +#: apt-pkg/pkgcache.cc:220 src/cmdline/cmdline_show.cc:323 +#: src/cmdline/cmdline_show.cc:322 +msgid "Obsoletes" +msgstr "Làm cũ" + +#: apt-pkg/pkgcache.cc:231 +msgid "important" +msgstr "quan trọng" + +#: apt-pkg/pkgcache.cc:231 +msgid "required" +msgstr "cần" + +#: ../partman-basicfilesystems.templates:147 +msgid "standard" +msgstr "chuẩn" + +#: apt-pkg/pkgcache.cc:232 +msgid "optional" +msgstr "tùy chọn" + +#: apt-pkg/pkgcache.cc:232 +msgid "extra" +msgstr "thêm" + +#: apt-pkg/depcache.cc:60 apt-pkg/depcache.cc:89 +msgid "Building dependency tree" +msgstr "Đang xây dụng cây cách phụ thuộc..." + +#: apt-pkg/depcache.cc:61 +msgid "Candidate versions" +msgstr "Phiên bản ứng cử" + +#: apt-pkg/depcache.cc:90 +msgid "Dependency generation" +msgstr "Tạo ra cách phụ thuộc" + +#: apt-pkg/tagfile.cc:73 apt-pkg/tagfile.cc:71 +#, c-format +msgid "Unable to parse package file %s (1)" +msgstr "Không thể phân tách tập tin gói %s (1)" + +#: apt-pkg/tagfile.cc:160 apt-pkg/tagfile.cc:158 +#, c-format +msgid "Unable to parse package file %s (2)" +msgstr "Không thể phân tách tập tin gói %s (2)" + +#: apt-pkg/sourcelist.cc:94 apt-pkg/sourcelist.cc:88 +#, c-format +msgid "Malformed line %lu in source list %s (URI)" +msgstr "Gặp dòng dạng sai %lu trong danh sách nguồn %s (địa chỉ Mạng)" + +#: apt-pkg/sourcelist.cc:96 apt-pkg/sourcelist.cc:90 +#, c-format +msgid "Malformed line %lu in source list %s (dist)" +msgstr "Gặp dòng dạng sai %lu trong danh sách nguồn %s (bản phân phối)" + +#: apt-pkg/sourcelist.cc:99 apt-pkg/sourcelist.cc:93 +#, c-format +msgid "Malformed line %lu in source list %s (URI parse)" +msgstr "" +"Gặp dòng dạng sai %lu trong danh sách nguồn %s (phân tách địa chỉ Mạng)." + +#: apt-pkg/sourcelist.cc:105 +#, c-format +msgid "Malformed line %lu in source list %s (absolute dist)" +msgstr "" +"Gặp dòng dạng sai %lu trong danh sách nguồn %s (bản phân phối tuyệt đối)" + +#: apt-pkg/sourcelist.cc:112 apt-pkg/sourcelist.cc:106 +#, c-format +msgid "Malformed line %lu in source list %s (dist parse)" +msgstr "" +"Gặp dòng dạng sai %lu trong danh sách nguồn %s (phân tách bản phân phối)" + +#: apt-pkg/sourcelist.cc:235 +#, c-format +msgid "Opening %s" +msgstr "Đang mở %s..." + +#: apt-pkg/sourcelist.cc:220 apt-pkg/cdrom.cc:426 apt-pkg/sourcelist.cc:249 +#, c-format +msgid "Line %u too long in source list %s." +msgstr "Dòng %u quá dài trong danh sách nguồn %s." + +#: apt-pkg/sourcelist.cc:240 apt-pkg/sourcelist.cc:266 +#, c-format +msgid "Malformed line %u in source list %s (type)" +msgstr "Gặp dòng dạng sai %u trong danh sách nguồn %s (kiểu)." + +#: apt-pkg/sourcelist.cc:244 apt-pkg/sourcelist.cc:270 +#, c-format +msgid "Type '%s' is not known on line %u in source list %s" +msgstr "Không biết kiểu « %s » trên dòng %u trong danh sách nguồn %s" + +#: apt-pkg/sourcelist.cc:252 apt-pkg/sourcelist.cc:255 +#: apt-pkg/sourcelist.cc:279 apt-pkg/sourcelist.cc:282 +#, c-format +msgid "Malformed line %u in source list %s (vendor id)" +msgstr "Gặp dòng dạng sai %u trong danh sách nguồn %s (mã nhận biết nhà bán)" + +#: apt-pkg/packagemanager.cc:402 +#, c-format +msgid "" +"This installation run will require temporarily removing the essential " +"package %s due to a Conflicts/Pre-Depends loop. This is often bad, but if " +"you really want to do it, activate the APT::Force-LoopBreak option." +msgstr "" +"Việc chạy tiến trình cài đặt này sẽ cần thiết gỡ bỏ tạm gói chủ yếu %s, do " +"vong lăp Xung đột/Phụ thuộc trước. Trường hợp này thường xấu, nhưng mà nếu " +"bạn thật sự muốn tiếp tục, có thể hoạt hóa tuy chọn « APT::Force-LoopBreak " +"» (buộc ngắt vòng lặp)." + +#: apt-pkg/pkgrecords.cc:37 +#, c-format +msgid "Index file type '%s' is not supported" +msgstr "Không hỗ trợ kiểu tập tin chỉ mục « %s »" + +#: apt-pkg/algorithms.cc:241 apt-pkg/algorithms.cc:238 +#, c-format +msgid "" +"The package %s needs to be reinstalled, but I can't find an archive for it." +msgstr "Cần phải cài đặt lại gói %s, nhưng mà không thể tìm kho cho nó." + +#: apt-pkg/algorithms.cc:1059 apt-pkg/algorithms.cc:1056 +msgid "" +"Error, pkgProblemResolver::Resolve generated breaks, this may be caused by " +"held packages." +msgstr "" +"Lỗi: « pkgProblemResolver::Resolve » (bộ tháo gỡ vấn đề gọi::tháo gỡ) đã tạo " +"ra nhiều chỗ ngắt, có lẽ một số gói đã giữ lại đã gây ra trường hợp này." + +#: apt-pkg/algorithms.cc:1061 apt-pkg/algorithms.cc:1058 +msgid "Unable to correct problems, you have held broken packages." +msgstr "Không thể sửa vấn đề, bạn đã giữ lại một số gói bị ngắt." + +#: apt-pkg/acquire.cc:62 apt-pkg/acquire.cc:61 +#, c-format +msgid "Lists directory %spartial is missing." +msgstr "Thiếu thư mục danh sách « %spartial »." + +#: apt-pkg/acquire.cc:66 apt-pkg/acquire.cc:65 +#, c-format +msgid "Archive directory %spartial is missing." +msgstr "Thiếu thư mục kho « %spartial »." + +#: apt-pkg/acquire.cc:821 +#, c-format +msgid "Downloading file %li of %li (%s remaining)" +msgstr "Đang tải về tập tin %li trên %li (%s còn lại)" + +#: apt-pkg/acquire-worker.cc:113 apt-pkg/acquire-worker.cc:112 +#, c-format +msgid "The method driver %s could not be found." +msgstr "Không tìm thấy trình điều khiển phương pháp %s." + +#: apt-pkg/acquire-worker.cc:162 apt-pkg/acquire-worker.cc:161 +#, c-format +msgid "Method %s did not start correctly" +msgstr "Phương pháp %s đã không bắt đầu cho đúng." + +#: apt-pkg/acquire-worker.cc:377 +#, c-format +msgid "Please insert the disc labeled: '%s' in the drive '%s' and press enter." +msgstr "Hãy nạp đĩa có nhãn « %s » vào ổ « %s » và bấm nút Enter." + +#: apt-pkg/init.cc:120 apt-pkg/init.cc:119 +#, c-format +msgid "Packaging system '%s' is not supported" +msgstr "Không hỗ trợ hệ thống đóng gói « %s »" + +#: apt-pkg/init.cc:136 apt-pkg/init.cc:135 +msgid "Unable to determine a suitable packaging system type" +msgstr "Không thể quyết định kiểu hệ thống đóng gói thích hợp" + +#: apt-pkg/clean.cc:61 +#, c-format +msgid "Unable to stat %s." +msgstr "Không thể lấy các thông tin về %s." + +#: apt-pkg/srcrecords.cc:48 apt-pkg/srcrecords.cc:49 +msgid "You must put some 'source' URIs in your sources.list" +msgstr "" +"Bạn phải để một số địa chỉ Mạng « nguồn » vào « sources.list » (danh sách " +"nguồn)" + +#: apt-pkg/cachefile.cc:73 src/generic/aptcache.cc:1580 +#: src/generic/aptcache.cc:1579 +msgid "The package lists or status file could not be parsed or opened." +msgstr "Không thể phân tách hay mở danh sách gói hay tâp tin trạng thái." + +#: apt-pkg/cachefile.cc:77 +msgid "You may want to run apt-get update to correct these problems" +msgstr "" +"Có lẽ bạn muốn chạy « apt-get update » (lấy cập nhật) để sửa các vấn đề này" + +#: apt-pkg/policy.cc:269 +msgid "Invalid record in the preferences file, no Package header" +msgstr "" +"Gặp mục ghi không hợp lệ trong tập tin tùy thích: không có phần đầu Package " +"(Gói)." + +#: apt-pkg/policy.cc:291 +#, c-format +msgid "Did not understand pin type %s" +msgstr "Không hiểu kiểu ghim %s" + +#: apt-pkg/policy.cc:299 +msgid "No priority (or zero) specified for pin" +msgstr "Chưa ghi rõ ưu tiên (hay số không) cho ghim" + +#: apt-pkg/pkgcachegen.cc:74 +msgid "Cache has an incompatible versioning system" +msgstr "Bộ nhớ tạm có hệ thống điêu khiển phiên bản không tương thích" + +#: apt-pkg/pkgcachegen.cc:117 +#, c-format +msgid "Error occurred while processing %s (NewPackage)" +msgstr "Gặp lỗi khi xử lý %s (NewPackage - gói mới)" + +#: apt-pkg/pkgcachegen.cc:129 +#, c-format +msgid "Error occurred while processing %s (UsePackage1)" +msgstr "Gặp lỗi khi xử lý %s (UsePackage1 - dùng gói 1)" + +#: apt-pkg/pkgcachegen.cc:150 +#, c-format +msgid "Error occurred while processing %s (UsePackage2)" +msgstr "Gặp lỗi khi xử lý %s (UsePackage2 - dùng gói 2)" + +#: apt-pkg/pkgcachegen.cc:154 +#, c-format +msgid "Error occurred while processing %s (NewFileVer1)" +msgstr "Gặp lỗi khi xử lý %s (NewFileVer1 - tập tin mới, phiên bản 1)" + +#: apt-pkg/pkgcachegen.cc:184 +#, c-format +msgid "Error occurred while processing %s (NewVersion1)" +msgstr "Gặp lỗi khi xử lý %s (NewVersion1 - phiên bản mới 1)" + +#: apt-pkg/pkgcachegen.cc:188 +#, c-format +msgid "Error occurred while processing %s (UsePackage3)" +msgstr "Gặp lỗi khi xử lý %s (UsePackage3 - dùng gói 3)" + +#: apt-pkg/pkgcachegen.cc:192 +#, c-format +msgid "Error occurred while processing %s (NewVersion2)" +msgstr "Gặp lỗi khi xử lý %s (NewVersion2 - phiên ban mới 2)" + +#: apt-pkg/pkgcachegen.cc:207 +msgid "Wow, you exceeded the number of package names this APT is capable of." +msgstr "Ồ, bạn đã vượt quá số tên gói mà trình APT này có thể quản lý." + +#: apt-pkg/pkgcachegen.cc:210 +msgid "Wow, you exceeded the number of versions this APT is capable of." +msgstr "Ồ, bạn đã vượt quá số phiên bản mà trình APT này có thể quản lý." + +#: apt-pkg/pkgcachegen.cc:213 +msgid "Wow, you exceeded the number of dependencies this APT is capable of." +msgstr "Ồ, bạn đã vượt quá số cách phụ thuộc mà trình APT này có thể quản lý." + +#: apt-pkg/pkgcachegen.cc:241 +#, c-format +msgid "Error occurred while processing %s (FindPkg)" +msgstr "Gặp lỗi khi xử lý %s (FindPkg - tìm gói)" + +#: apt-pkg/pkgcachegen.cc:254 +#, c-format +msgid "Error occurred while processing %s (CollectFileProvides)" +msgstr "" +"Gặp lỗi khi xử lý %s (CollectFileProvides - tập hợp các trường hợp miễn là " +"một tập tin)" + +#: apt-pkg/pkgcachegen.cc:260 +#, c-format +msgid "Package %s %s was not found while processing file dependencies" +msgstr "Không tìm thấy gói %s %s khi xử lý cách phụ thuộc của/vào tập tin" + +#: apt-pkg/pkgcachegen.cc:574 +#, c-format +msgid "Couldn't stat source package list %s" +msgstr "Không thể lấy các thông tin về danh sách gói nguồn %s" + +#: apt-pkg/pkgcachegen.cc:658 +msgid "Collecting File Provides" +msgstr "Đang tập hợp các trường hợp « tập tin miễn là »" + +#: apt-pkg/pkgcachegen.cc:785 apt-pkg/pkgcachegen.cc:792 +#: apt-pkg/pkgcachegen.cc:774 apt-pkg/pkgcachegen.cc:781 +msgid "IO Error saving source cache" +msgstr "Lỗi nhập/xuất khi lưu bộ nhớ tạm nguồn" + +#: apt-pkg/acquire-item.cc:126 apt-pkg/acquire-item.cc:124 +#, c-format +msgid "rename failed, %s (%s -> %s)." +msgstr "việc thay đổi tên bị lỗi, %s (%s → %s)." + +#: apt-pkg/acquire-item.cc:236 apt-pkg/acquire-item.cc:950 +#: apt-pkg/acquire-item.cc:511 +msgid "MD5Sum mismatch" +msgstr "MD5Sum (tổng kiểm) không khớp được" + +#: apt-pkg/acquire-item.cc:645 +msgid "There are no public key available for the following key IDs:\n" +msgstr "Không có khoá công sẵn sàng cho những ID khoá theo đây:\n" + +#: apt-pkg/acquire-item.cc:758 src/generic/pkg_acqfile.cc:86 +#: apt-pkg/acquire-item.cc:353 +#, c-format +msgid "" +"I wasn't able to locate a file for the %s package. This might mean you need " +"to manually fix this package. (due to missing arch)" +msgstr "" +"Không tìm thấy tập tin liên quan đến gói %s. Có lẽ bạn cần phải tự sửa gói " +"này, do thiếu kiến trúc." + +#: apt-pkg/acquire-item.cc:817 apt-pkg/acquire-item.cc:388 +#, c-format +msgid "" +"I wasn't able to locate file for the %s package. This might mean you need to " +"manually fix this package." +msgstr "" +"Không tìm thấy tập tin liên quan đến gói %s. Có lẽ bạn cần phải tự sửa gói " +"này." + +#: apt-pkg/acquire-item.cc:853 src/generic/pkg_acqfile.cc:134 +#: apt-pkg/acquire-item.cc:419 +#, c-format +msgid "" +"The package index files are corrupted. No Filename: field for package %s." +msgstr "" +"Các tập tin chỉ mục của gói này bị hỏng. Không có trường Filename: (Tên tập " +"tin:) cho gói %s." + +#: apt-pkg/acquire-item.cc:940 apt-pkg/acquire-item.cc:501 +msgid "Size mismatch" +msgstr "Kích cỡ không khớp được" + +#: apt-pkg/vendorlist.cc:66 +#, c-format +msgid "Vendor block %s contains no fingerprint" +msgstr "Khối nhà bán %s không chứa vân tay" + +#: apt-pkg/cdrom.cc:507 +#, c-format +msgid "" +"Using CD-ROM mount point %s\n" +"Mounting CD-ROM\n" +msgstr "" +"Đang dùng điểm lắp đĩa CD-ROM %s\n" +"Đang lắp đĩa CD-ROM...\n" + +#: apt-pkg/cdrom.cc:516 apt-pkg/cdrom.cc:598 +msgid "Identifying.. " +msgstr "Đang nhận diện... " + +#: apt-pkg/cdrom.cc:541 +#, c-format +msgid "Stored label: %s \n" +msgstr "Nhãn đã lưu : %s\n" + +#: apt-pkg/cdrom.cc:561 +#, c-format +msgid "Using CD-ROM mount point %s\n" +msgstr "Đang dùng điểm lắp đĩa CD-ROM %s\n" + +#: apt-pkg/cdrom.cc:579 +msgid "Unmounting CD-ROM\n" +msgstr "Đang tháo lắp đĩa CD-ROM...\n" + +#: apt-pkg/cdrom.cc:583 +msgid "Waiting for disc...\n" +msgstr "Đang đợi đĩa...\n" + +#. Mount the new CDROM +#: apt-pkg/cdrom.cc:591 +msgid "Mounting CD-ROM...\n" +msgstr "Đang lắp đĩa CD-ROM...\n" + +#: apt-pkg/cdrom.cc:609 +msgid "Scanning disc for index files..\n" +msgstr "Đang quét đĩa tìm tập tin chỉ mục...\n" + +#: apt-pkg/cdrom.cc:647 +#, c-format +msgid "Found %i package indexes, %i source indexes and %i signatures\n" +msgstr "Mới tìm %i chỉ mục gói, %i chỉ mục nguồn và %i chữ ký\n" + +#: apt-pkg/cdrom.cc:710 +msgid "That is not a valid name, try again.\n" +msgstr "Nó không phải là một tên hợp lệ: hãy thử lại.\n" + +#: apt-pkg/cdrom.cc:726 +#, c-format +msgid "" +"This disc is called: \n" +"'%s'\n" +msgstr "" +"Tên đĩa này:\n" +"%s\n" + +#: apt-pkg/cdrom.cc:730 +msgid "Copying package lists..." +msgstr "Đang sao chép các danh sách gói..." + +#: apt-pkg/cdrom.cc:754 +msgid "Writing new source list\n" +msgstr "Đang ghi danh sách nguồn mới...\n" + +#: apt-pkg/cdrom.cc:763 +msgid "Source list entries for this disc are:\n" +msgstr "Các mục nhập danh sách nguồn cho đĩa này:\n" + +#: apt-pkg/cdrom.cc:803 +msgid "Unmounting CD-ROM..." +msgstr "Đang tháo lắp đĩa CD-ROM..." + +#: apt-pkg/indexcopy.cc:261 +#, c-format +msgid "Wrote %i records.\n" +msgstr "Mới ghi %i mục ghi.\n" + +#: apt-pkg/indexcopy.cc:263 +#, c-format +msgid "Wrote %i records with %i missing files.\n" +msgstr "Mới ghi %i mục ghi với %i tập tin còn thiếu.\n" + +#: apt-pkg/indexcopy.cc:266 +#, c-format +msgid "Wrote %i records with %i mismatched files\n" +msgstr "Mới ghi %i mục ghi với %i tập tin không khớp với nhau\n" + +#: apt-pkg/indexcopy.cc:269 +#, c-format +msgid "Wrote %i records with %i missing files and %i mismatched files\n" +msgstr "" +"Mới ghi %i mục ghi với %i tập tin còn thiếu và %i tập tin không khớp với " +"nhau\n" + +#: apt-pkg/deb/dpkgpm.cc:359 +#, c-format +msgid "Unpacking %s" +msgstr "Đang mở gói %s..." + +#: apt-pkg/deb/dpkgpm.cc:364 +#, c-format +msgid "Preparing to configure %s" +msgstr "Đang chuẩn bị cấu hình %s..." + +#: apt-pkg/deb/dpkgpm.cc:365 ../Debconf/FrontEnd.pm:203 ../hwconf.c:833 +#, fuzzy, c-format, perl-format +msgid "Configuring %s" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Đang cấu hình %s...\n" +"#-#-#-#-# kudzu_1.1.67-1_vi.po (kudzu VERSION) #-#-#-#-#\n" +"Đang cấu hình %s" + +#: apt-pkg/deb/dpkgpm.cc:366 +#, c-format +msgid "Installed %s" +msgstr "Đã cài đặt %s" + +#: apt-pkg/deb/dpkgpm.cc:371 +#, c-format +msgid "Preparing for removal of %s" +msgstr "Đang chuẩn bị gỡ bỏ %s..." + +#: apt-pkg/deb/dpkgpm.cc:372 +#, c-format +msgid "Removing %s" +msgstr "Đang gỡ bỏ %s..." + +#: apt-pkg/deb/dpkgpm.cc:373 +#, c-format +msgid "Removed %s" +msgstr "Đã gỡ bỏ %s" + +#: apt-pkg/deb/dpkgpm.cc:378 +#, c-format +msgid "Preparing for remove with config %s" +msgstr "Đang chuẩn bị gỡ bỏ với cấu hình %s..." + +#: apt-pkg/deb/dpkgpm.cc:379 +#, c-format +msgid "Removed with config %s" +msgstr "Mới gỡ bỏ với cấu hình %s" + +#: methods/rsh.cc:330 +msgid "Connection closed prematurely" +msgstr "Kết nối bị đóng quá sớm." + +#: src/c.l:137 +msgid "unterminated string?" +msgstr "chuỗi không được chấm dứt không?" + +#: src/c.l:296 +#, c-format +msgid "Command line: %s\n" +msgstr "Dòng lệnh: %s\n" + +#: src/c.l:299 +#, c-format +msgid "cannot execute `%s'" +msgstr "không thể thực hiện « %s »" + +#: src/c.l:342 src/rc.c:60 +#, c-format +msgid "cannot open `%s'" +msgstr "không thể mở « %s »" + +#: src/c.l:422 +#, c-format +msgid "New location: %s:%d\n" +msgstr "Địa điểm mới: %s:%d\n" + +#. TRANSLATORS: Please, preserve the vertical tabulation (^K character) +#. in this message +#: src/main.c:29 +msgid "" +"generate a program flowgraph * The effect of each option marked with an " +"asterisk is reversed if the option's long name is prefixed with `no-'. For " +"example, --no-cpp cancels --cpp." +msgstr "" +"tạo ra một lưọc đồ chương trình * Hiệu ứng của mỗi tùy chọn có dấu sao có " +"được đảo ngược nếu tên dài của tùy chọn có tiền tố « no- ». Lấy thí dụ, tùy " +"chọn « --no-cpp cancels » hủy « --cpp »." + +#: src/main.c:56 +msgid "General options:" +msgstr "Tùy chọn chung:" + +#: src/main.c:57 src/main.c:98 +#: ../addressbook/tools/evolution-addressbook-export.c:63 src/main.c:107 +#: ../gnomine/gnomine.c:862 ../gtali/setup.c:85 ../gtali/setup.c:86 +#: ../same-gnome/same-gnome.c:128 ../gsmclient/gsmclient-test.c:153 +msgid "NUMBER" +msgstr "SỐ" + +#: src/main.c:58 +msgid "Set the depth at which the flowgraph is cut off" +msgstr "Lập độ sâu mà lược đồ bị cắt ra" + +#: src/main.c:59 +msgid "CLASSES" +msgstr "HẠNG" + +#: src/main.c:60 +msgid "" +"Include specified classes of symbols (see below). Prepend CLASSES with ^ or " +"- to exclude them from the output" +msgstr "" +"Gồm một số hạn ký hiệu đã ghi rõ (xem dưới). Them dấu mũ « ^ » hay dấu trừ « " +"- » trước các hạng bạn muốn trừ ra dữ liệu xuất." + +#: ../bonobo/bonobo-ui-init-gtk.c:138 ../gdk/gdk.c:119 lib/argp-parse.c:84 +msgid "NAME" +msgstr "TÊN" + +#: src/main.c:62 +msgid "" +"Use given output format NAME. Valid names are `gnu' (default) and `posix'" +msgstr "" +"Dùng TÊN khuôn dạng xuất đã cho. Tên hợp lệ là « gnu » (mặc định) và « posix " +"»" + +#: src/main.c:65 +msgid "* Print reverse call tree" +msgstr "* In ra cây gọi đảo ngược" + +#: src/main.c:67 +msgid "Produce cross-reference listing only" +msgstr "Cung cấp chỉ danh sách tham chiếu chéo thôi" + +#: src/main.c:68 +msgid "OPT" +msgstr "TCH" + +#: src/main.c:69 +msgid "" +"Set printing option to OPT. Valid OPT values are: xref (or cross-ref), tree. " +"Any unambiguous abbreviation of the above is also accepted" +msgstr "" +"Lập tùy chọn in thành TCH. Giá trị TCH hợp lệ là « xref » (tham chiếu chéo) " +"và « tree » (cây). Cũng chấp nhận được bất cứ từ viết tắt rõ ràng nào của " +"chúng." + +#: ../gncal/gnomecal-main.c:94 +msgid "FILE" +msgstr "TẬP TIN" + +#: src/main.c:72 +msgid "Set output file name (default -, meaning stdout)" +msgstr "" +"Lập tên tập tin xuất (mặc định là « - » mà có nghĩa là thiết bị xuất chuẩn)" + +#: src/main.c:75 +msgid "Symbols classes for --include argument" +msgstr "Hạng ký hiệu cho đối số « --include » (gồm)" + +#: src/main.c:77 +msgid "all data symbols, both external and static" +msgstr "mọi ký hiệu dữ liệu, cả kiểu bên ngoài lẫn kiểu tĩnh đều" + +#: src/main.c:79 +msgid "symbols whose names begin with an underscore" +msgstr "ký hiệu có tên bắt đầu với dấu gạch dưới « _ »" + +#: src/main.c:81 +msgid "static symbols" +msgstr "ký hiệu tĩnh" + +#: src/main.c:83 +msgid "typedefs (for cross-references only)" +msgstr "typedef (lời định nghĩa kiểu : chỉ cho tham chiếu chéo)" + +#: src/main.c:89 +msgid "Parser control:" +msgstr "Điều khiển bộ phân tách:" + +#: src/main.c:91 +msgid "* Rely on indentation" +msgstr "* Sở cậy ở thụt lề" + +#: src/main.c:95 +msgid "* Accept only sources in ANSI C" +msgstr "* Chấp nhận chỉ mã nguồn bằng ANSI C" + +#: src/main.c:99 +msgid "Set initial token stack size to NUMBER" +msgstr "Lập kích cỡ ngăn nhớ ban đầu là SỐ" + +#: src/main.c:100 +msgid "SYMBOL:TYPE" +msgstr "KÝ HIỆU: KIỂU" + +#: src/main.c:101 +msgid "" +"Register SYMBOL with given TYPE. Valid types are: keyword (or kw), modifier, " +"identifier, type, wrapper. Any unambiguous abbreviation of the above is also " +"accepted" +msgstr "" +"Đăng ký KÝ HIỆU với KIỂU đã cho. Kiểu hợp lệ là:\n" +" • keyword (hay kw)\ttừ khoá\n" +" • modifier\t\t\tbộ sửa đổi\n" +" • identifier\t\t\tbộ nhận diện\n" +" • type\t\t\t\tkiểu\n" +" • wrapper\t\t\tbộ bao bọc\n" +"Cũng chấp nhận bất cứ từ viết tất rõ ràng nào của điều ở trên." + +#: src/main.c:103 +msgid "Assume main function to be called NAME" +msgstr "Giả sử hàm chính sẽ có tên TÊN." + +#: src/main.c:104 +msgid "NAME[=DEFN]" +msgstr "TÊN[=LỜI_ĐN]" + +#: src/main.c:105 +msgid "Predefine NAME as a macro" +msgstr "Định nghĩa sẵn TÊN là bộ lệnh (macrô)" + +#: src/main.c:107 +msgid "Cancel any previous definition of NAME" +msgstr "Hủy bất cứ lời định nghĩa trước nào của TÊN" + +#: src/main.c:108 ../utils/nautilus-actions-convert.c:44 +msgid "DIR" +msgstr "TMỤC" + +#: src/main.c:109 +msgid "" +"Add the directory DIR to the list of directories to be searched for header " +"files." +msgstr "" +"Thêm thư mục TMỤC vào danh sách các thư mục nơi cần tìm kiếm tập tin phần " +"đầu." + +#: src/main.c:110 src/main.c:117 ../src/main.c:88 ../tools/gnomesu.c:41 +#: ../gnome-netinfo/main.c:82 +msgid "COMMAND" +msgstr "LỆNH" + +#: src/main.c:111 +msgid "* Run the specified preprocessor command" +msgstr "* Chạy lệnh bộ tiền xử lý đã ghi rõ" + +#: src/main.c:119 +msgid "Output control:" +msgstr "Điều khiển xuất:" + +#: src/main.c:121 +msgid "* Print line numbers" +msgstr "* In ra số thứ tự dòng" + +#: src/main.c:125 +msgid "* Print nesting level along with the call tree" +msgstr "* In ra cấp lồng nhau cùng với cây gọi" + +#: src/main.c:129 +msgid "Control graph appearance" +msgstr "Điều khiển hình thức của đồ thị" + +#: src/main.c:131 +msgid "* Draw ASCII art tree" +msgstr "* Vẽ cây nghệ ASCII" + +#: src/main.c:135 +msgid "* Brief output" +msgstr "* Xuất ngắn" + +#: src/main.c:139 +msgid "* Additionally format output for use with GNU Emacs" +msgstr "* Cũng định dạng dữ liệu xuất để sử dụng với Emacs của GNU" + +#: src/main.c:143 +msgid "* Do not print argument lists in function declarations" +msgstr "* Đừng in ra danh sách đối số trong lời tuyên bố hàm" + +#: src/main.c:147 +msgid "* Do not print symbol names in declaration strings" +msgstr "* Đừng in ra tên ký hiệu trong chuỗi tuyên bố" + +#: src/main.c:153 +msgid "Informational options:" +msgstr "Tùy chọn thông tin:" + +#: src/main.c:155 +msgid "* Verbose error diagnostics" +msgstr "* Chẩn đoán lỗi một cách chi tiết" + +#: src/main.c:159 src/main.c:200 +msgid "Print license and exit" +msgstr "In ra Quyền phép rồi thoát." + +#: src/main.c:161 +msgid "Set debugging level" +msgstr "Lập cấp gỡ lỗi" + +#: src/main.c:167 +msgid "" +" GNU cflow is free software; you can redistribute it and/or modify\n" +" it under the terms of the GNU General Public License as published by\n" +" the Free Software Foundation; either version 2 of the License, or\n" +" (at your option) any later version.\n" +"\n" +" GNU cflow is distributed in the hope that it will be useful,\n" +" but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +" GNU General Public License for more details.\n" +"\n" +" You should have received a copy of the GNU General Public License\n" +" along with GNU cflow; if not, write to the Free Software Foundation,\n" +" Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" +"\n" +"\n" +msgstr "" +" Trình cflow của GNU là phần mềm tự do nên có thể phân phối nó lại\n" +" và sửa đổi nó theo điều kiện của Quyền Công Chung Gnu (GPL)\n" +" như xuất do Tổ chức Phần mềm Tự do (Free Software Foundation),\n" +" hoặc phiên bản 2 của quyền ấy, hoặc (tùy chọn) bất cứ phiên bản sau nào.\n" +"\n" +" Chúng tôi phân phối trình cflow của GNU vì mong nó có ích, nhưng\n" +" không có bảo đảm gi cả, không có bảo đảm ngụ ý khả năng bán\n" +" hay khả năng làm việc dứt khoát.\n" +" Hãy xem Quyền Công Chung Gnu (GPL) để tim chi tiết.\n" +"\n" +" Nếu bạn chưa nhận một bản sao Quyền Công Chung Gnu (GPL)\n" +" thì hãy viết cho Tổ chức Phần mềm Tự do:\n" +" Free Software Foundation, Inc.,\n" +" 51 Franklin Street, Fifth Floor,\n" +" Boston, MA 02110-1301 USA (Mỹ)\n" +"\n" + +#: src/main.c:281 +#, c-format +msgid "unknown symbol type: %s" +msgstr "không biết kiểu ký hiệu : %s" + +#: src/main.c:310 +#, c-format +msgid "unknown print option: %s" +msgstr "không biết tùy chọn in: %s" + +#: src/main.c:433 src/main.c:442 +msgid "level indent string is too long" +msgstr "chuỗi thụt lề cấp quá dài" + +#: src/main.c:470 +msgid "level-indent syntax" +msgstr "cú pháp thụt lề cấp" + +#: src/main.c:494 +#, c-format +msgid "unknown level indent option: %s" +msgstr "không biết tùy chọn thụt lề cấp: %s" + +#: src/main.c:529 +#, c-format +msgid "" +"License for %s:\n" +"\n" +msgstr "Quyền Phép cho %s:\\n\n" + +#: src/main.c:575 src/main.c:760 +#, c-format +msgid "%s: No such output driver" +msgstr "%s: Không có trình điều khiển xuất như vậy" + +#: src/main.c:602 +#, c-format +msgid "Unknown symbol class: %c" +msgstr "Không biết hạng ký hiệu : %c" + +#: src/main.c:682 +msgid "[FILE]..." +msgstr "[TẬP_TIN]..." + +#: src/main.c:725 ../process.c:911 +msgid "Exiting" +msgstr "Đang thoát..." + +#: src/main.c:792 +msgid "no input files" +msgstr "không có tập tin xuất nào" + +#: src/parser.c:119 +#, c-format +msgid " near " +msgstr " gần " + +#: src/parser.c:188 +msgid "INTERNAL ERROR: cannot return token to stream" +msgstr "LỖI NỘI BỘ: không thể trả gởi hiệu bài về luồng" + +#: src/parser.c:398 +msgid "unexpected end of file in expression" +msgstr "kết thúc tập tin bất ngờ trong biểu thức" + +#: src/parser.c:453 src/parser.c:552 +msgid "expected `;'" +msgstr "ngờ dấu chấm phẩy « ; »" + +#: src/parser.c:470 src/parser.c:577 +msgid "unexpected end of file in declaration" +msgstr "kết thức tập tin bất ngờ trong lời tuyên bố" + +#: src/parser.c:502 +msgid "missing `;' after struct declaration" +msgstr "thiếu dấu chấm phẩy « ; » sau lời tuyên bố « struct »" + +#: src/parser.c:599 +msgid "unexpected end of file in initializer list" +msgstr "kết thức tập tin bất ngờ trong danh sách bộ khởi động" + +#: src/parser.c:683 +msgid "unexpected end of file in struct" +msgstr "kết thúc tập tin bất ngờ trong « struct »" + +#: src/parser.c:769 src/parser.c:792 +msgid "expected `)'" +msgstr "ngờ dấu đóng ngoặc « ) »" + +#: src/parser.c:805 +msgid "unexpected end of file in function declaration" +msgstr "kết thức tập tin bất ngờ trong lời tuyên bố hàm" + +#: src/parser.c:877 +msgid "unexpected token in parameter list" +msgstr "hiệu bài bất ngờ trong danh sách tham số" + +#: src/parser.c:892 +msgid "unexpected end of file in parameter list" +msgstr "kết thúc tập tin bất ngờ trong danh sách tham số" + +#: src/parser.c:930 +msgid "forced function body close" +msgstr "việc đóng thân hàm bị buộc" + +#: src/parser.c:944 +msgid "unexpected end of file in function body" +msgstr "kết thức tập tin bất ngờ trong thân hàm" + +#: src/parser.c:979 +#, c-format +msgid "%s/%d redefined" +msgstr "%s/%d được định nghĩa lại" + +#: src/parser.c:982 +msgid "this is the place of previous definition" +msgstr "đây là vị trí của lời định nghĩa trước" + +#: src/parser.c:994 +#, c-format +msgid "%s:%d: %s/%d defined to %s\n" +msgstr "%s:%d: %s/%d được định nghĩa thành %s\n" + +# Variable: do not translate/ biến: đừng dịch +#: src/parser.c:1019 +#, c-format +msgid "%s:%d: type %s\n" +msgstr "%s:%d: kiểu %s\n" + +#: src/rc.c:55 +msgid "not enough memory to process rc file" +msgstr "không đủ bộ nhớ để xử lý tập tin « rc » (tài nguyên)" + +#: src/symbol.c:317 +msgid "not enough core" +msgstr "không đủ lõi" + +#: lib/argp-help.c:195 lib/argp-help.c:194 +#, c-format +msgid "%.*s: ARGP_HELP_FMT parameter requires a value" +msgstr "%.*s: tham số « ARGP_HELP_FMT » cần thiết giá trị" + +#: lib/argp-help.c:204 lib/argp-help.c:203 +#, c-format +msgid "%.*s: Unknown ARGP_HELP_FMT parameter" +msgstr "%.*s: không biết tham số « ARGP_HELP_FMT »" + +#: lib/argp-help.c:216 lib/argp-help.c:215 +#, c-format +msgid "Garbage in ARGP_HELP_FMT: %s" +msgstr "Rác trong « ARGP_HELP_FMT »: %s" + +#: lib/argp-help.c:1195 lib/argp-help.c:1194 +msgid "" +"Mandatory or optional arguments to long options are also mandatory or " +"optional for any corresponding short options." +msgstr "" +"Tất cả đối số bắt buộc phải sử dụng với tùy chọn dài cũng bắt buộc với tùy " +"chọn ngắn tương ứng." + +#: lib/argp-help.c:1582 gphoto2/shell.c:747 ../glib/goption.c:468 +#: lib/argp-help.c:1581 schroot/schroot-options.cc:126 +#: schroot/schroot-releaselock-options.cc:68 +#, c-format +msgid "Usage:" +msgstr "Cách sử dụng:" + +#: lib/argp-help.c:1586 lib/argp-help.c:1585 +msgid " or: " +msgstr " hoặc " + +#: lib/argp-help.c:1598 lib/argp-help.c:1597 +msgid " [OPTION...]" +msgstr " [TÙY_CHỌN...]" + +#: lib/argp-help.c:1625 lib/argp-help.c:1624 lib/print_error.c:35 +#: src/rpasswd.c:127 src/rpasswdd.c:146 +#, c-format +msgid "Try `%s --help' or `%s --usage' for more information.\n" +msgstr "" +"Hãy thử lệnh « %s --help » (trợ giúp) hoặc lệnh « %s --usage » (cách sử " +"dụng) để xem thông tin thêm.\n" + +#: lib/argp-help.c:1873 lib/error.c:122 lib/error.c:131 lib/error.c:159 +#: lib/error.c:121 lib/argp-help.c:1872 src/err-codes.h:229 +msgid "Unknown system error" +msgstr "Gặp lỗi hệ thống không rõ" + +#: lib/argp-parse.c:83 src/main.c:198 lib/argp-parse.c:82 +msgid "Give this help list" +msgstr "Hiển thị trợ giúp này" + +#: lib/argp-parse.c:84 src/main.c:199 lib/argp-parse.c:83 +msgid "Give a short usage message" +msgstr "Hiển thị thông điệp cách sử dụng ngắn" + +#: lib/argp-parse.c:85 lib/argp-parse.c:84 +msgid "Set the program name" +msgstr "Lập tên chương trình" + +#: lib/argp-parse.c:87 lib/argp-parse.c:86 +msgid "Hang for SECS seconds (default 3600)" +msgstr "Treo trong vòng GIÂY giây (mặc định là 3600)" + +#: lib/argp-parse.c:148 src/main.c:201 lib/argp-parse.c:147 +msgid "Print program version" +msgstr "In ra phiên bản chương trình" + +#: lib/argp-parse.c:164 lib/argp-parse.c:163 +msgid "(PROGRAM ERROR) No version known!?" +msgstr "(LỖI CHƯƠNG TRÌNH) Không biết phiên bản không?" + +#: lib/argp-parse.c:620 lib/argp-parse.c:619 +#, c-format +msgid "%s: Too many arguments\n" +msgstr "%s: Quá nhiều đối số\n" + +#: lib/argp-parse.c:763 lib/argp-parse.c:762 +msgid "(PROGRAM ERROR) Option should have been recognized!?" +msgstr "(LỖI CHƯƠNG TRÌNH) Nên nhận diện tùy chọn mà chưa?" + +#: lib/getopt.c:552 lib/getopt.c:571 src/main/getopt.c:681 lib/getopt.c:551 +#: lib/getopt.c:570 lib/getopt.c:694 share/getopt.c:673 getopt.c:663 +#, c-format +msgid "%s: option `%s' is ambiguous\n" +msgstr "%s: tùy chọn « %s » là mơ hồ\n" + +#: lib/getopt.c:604 lib/getopt.c:608 src/main/getopt.c:706 lib/getopt.c:603 +#: lib/getopt.c:607 lib/getopt.c:719 share/getopt.c:698 getopt.c:687 +#, c-format +msgid "%s: option `--%s' doesn't allow an argument\n" +msgstr "%s: tùy chọn « --%s » không cho phép đối số\n" + +#: lib/getopt.c:617 lib/getopt.c:622 src/main/getopt.c:712 lib/getopt.c:616 +#: lib/getopt.c:621 lib/getopt.c:724 share/getopt.c:703 getopt.c:692 +#, c-format +msgid "%s: option `%c%s' doesn't allow an argument\n" +msgstr "%s: tùy chọn « %c%s » không cho phép đối số\n" + +#: lib/getopt.c:915 share/getopt.c:721 share/getopt.c:894 getopt.c:709 +#: getopt.c:882 +#, c-format +msgid "%s: option `%s' requires an argument\n" +msgstr "%s: tùy chọn « %s » cần đến đối số\n" + +#: lib/getopt.c:730 lib/getopt.c:771 share/getopt.c:750 getopt.c:738 +#, c-format +msgid "%s: unrecognized option `--%s'\n" +msgstr "%s: không nhận ra tùy chọn « --%s »\n" + +#: lib/getopt.c:741 lib/getopt.c:775 share/getopt.c:754 getopt.c:742 +#, c-format +msgid "%s: unrecognized option `%c%s'\n" +msgstr "%s: không nhận ra tùy chọn « %c%s »\n" + +#: lib/getopt.c:799 lib/getopt.c:801 share/getopt.c:780 getopt.c:768 +#, c-format +msgid "%s: illegal option -- %c\n" +msgstr "%s: không cho phép tùy chọn « -- %c »\n" + +#: lib/getopt.c:808 lib/getopt.c:804 share/getopt.c:783 getopt.c:771 +#, c-format +msgid "%s: invalid option -- %c\n" +msgstr "%s: tùy chọn không hợp lệ « -- %c »\n" + +#: lib/getopt.c:964 share/getopt.c:813 share/getopt.c:943 getopt.c:801 +#: getopt.c:931 +#, c-format +msgid "%s: option requires an argument -- %c\n" +msgstr "%s: tùy chọn cần đến đối số « -- %c »\n" + +#: lib/getopt.c:954 lib/getopt.c:881 share/getopt.c:860 getopt.c:848 +#, c-format +msgid "%s: option `-W %s' is ambiguous\n" +msgstr "%s: tùy chọn « -W %s » là mơ hồ\n" + +#: lib/getopt.c:999 lib/getopt.c:899 share/getopt.c:878 getopt.c:866 +#, c-format +msgid "%s: option `-W %s' doesn't allow an argument\n" +msgstr "%s: tùy chọn « -W %s » không cho phép đối số\n" + +#: lib/obstack.c:441 lib/xalloc-die.c:38 lib/xsetenv.c:40 +msgid "memory exhausted" +msgstr "hết bộ nhớ hoàn toàn" + +#: ../level/aceticacid.atomix.xml.h:1 +msgid "Acetic Acid" +msgstr "Axit axetic" + +#: ../level/acetone.atomix.xml.h:1 +msgid "Acetone" +msgstr "Axetôn" + +#: ../level/butanol.atomix.xml.h:1 +msgid "Butanol" +msgstr "Butanola" + +#: ../level/cyclobutane.atomix.xml.h:1 +msgid "Cyclobutane" +msgstr "Xiclôbutan" + +#: ../level/dimethylether.atomix.xml.h:1 +msgid "Dimethyl Ether" +msgstr "Ête metyla đôi" + +#: ../level/ethanal.atomix.xml.h:1 +msgid "Ethanal" +msgstr "Etanan" + +#: ../level/ethane.atomix.xml.h:1 +msgid "Ethane" +msgstr "Etan" + +#: ../level/ethanol.atomix.xml.h:1 +msgid "Ethanol" +msgstr "Etanola" + +#: ../level/ethylene.atomix.xml.h:1 +msgid "Ethylene" +msgstr "Etylen" + +#: ../level/glycerin.atomix.xml.h:1 +msgid "Glycerin" +msgstr "Glyxerin" + +#: ../level/lactic-acid.atomix.xml.h:1 +msgid "Lactic Acid" +msgstr "Acit lactic" + +#: ../level/methanal.atomix.xml.h:1 +msgid "Methanal" +msgstr "Metanan" + +#: ../level/methane.atomix.xml.h:1 +msgid "Methane" +msgstr "Metan" + +#: ../level/methanol.atomix.xml.h:1 +msgid "Methanol" +msgstr "Metanola" + +#: ../level/propanal.atomix.xml.h:1 +msgid "Propanal" +msgstr "Prôpanan" + +#: ../level/propylene.atomix.xml.h:1 +msgid "Propylene" +msgstr "Prôpylen" + +#: ../level/pyran.atomix.xml.h:1 +msgid "Pyran" +msgstr "Pyran" + +#: ../level/transbutylen.atomix.xml.h:1 +msgid "Trans Butylen" +msgstr "Butylen qua" + +#: ../level/water.atomix.xml.h:1 +msgid "Water" +msgstr "Nước" + +#: ../src/atomix-ui.xml.h:1 +msgid "Continue paused game" +msgstr "Tiếp tục chơi" + +#: ../src/atomix-ui.xml.h:2 +msgid "End a game" +msgstr "Kết thúc trò chơi" + +#: ../src/atomix-ui.xml.h:3 +msgid "Pause the running game" +msgstr "Tạm dừng trò chơi" + +#: ../src/atomix-ui.xml.h:4 +msgid "Reset level" +msgstr "Đặt lại cấp độ" + +#: ../src/atomix-ui.xml.h:5 +msgid "Restores start situation" +msgstr "Phục hồi vị trí ban đầu" + +#: ../src/atomix-ui.xml.h:6 +msgid "Set preferences" +msgstr "Thiết lập thông số" + +#: ../src/atomix-ui.xml.h:7 +msgid "Skip _level" +msgstr "Bỏ qua _cấp độ" + +#: ../src/atomix-ui.xml.h:8 +msgid "Skip the current level" +msgstr "Bỏ qua cấp độ này" + +#: ../src/atomix-ui.xml.h:9 +msgid "Start a new game" +msgstr "Bắt đầu chơi" + +#: ../src/atomix-ui.xml.h:10 +msgid "Undo the last move" +msgstr "Hoàn lại lần đi cuối" + +#: ../src/atomix-ui.xml.h:11 +msgid "View highscores" +msgstr "Xem điểm cao" + +#: ../src/atomix-ui.xml.h:12 +msgid "_Continue game" +msgstr "_Tiếp tục chơi" + +#: ../src/atomix-ui.xml.h:13 +msgid "_End Game" +msgstr "_Kết thúc trò chơi" + +#: ../src/atomix-ui.xml.h:14 +msgid "_Game" +msgstr "_Trò chơi" + +#. #-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-# +#. Help +#. #-#-#-#-# NetworkManager.vi.po (NetworkManager HEAD) #-#-#-#-# +#. Help item +#: ../pan/save-ui.c:262 ../Pyblio/GnomeUI/Document.py:149 src/mainwin.cpp:549 +#: ../src/glade-gtk.c:2317 ../gnome/applet/applet.c:2208 po/silky.glade.h:216 +#: app/menubar.c:691 +msgid "_Help" +msgstr "Trợ _giúp" + +#: ../src/atomix-ui.xml.h:21 +msgid "_New Game" +msgstr "Trò chơi _mới" + +#: ../src/atomix-ui.xml.h:22 +msgid "_Pause game" +msgstr "Tạm _dừng trò chơi" + +#: ../src/atomix-ui.xml.h:23 +msgid "_Preferences ..." +msgstr "_Tùy thích..." + +#: ../src/atomix-ui.xml.h:24 +msgid "_Scores ..." +msgstr "Đ_iểm..." + +#: ../src/atomix-ui.xml.h:25 +msgid "_Undo move" +msgstr "_Hoàn lại nước đi" + +#: ../src/level-manager.c:174 +msgid "Couldn't find level sequence description." +msgstr "Không thể tìm chuỗi mô tả cấp độ." + +#: ../src/level-manager.c:188 +msgid "No level found." +msgstr "Không tìm thấy cấp độ." + +#: ../src/level-manager.c:284 +#, c-format +msgid "Found level '%s' in: %s" +msgstr "Tìm thấy cấp độ « %s » trong: %s" + +#: ../src/main.c:126 +msgid "You have not achieved any scores yet. Play a little before coming back!" +msgstr "Bạn chưa được điểm nào. Chơi nữa nhé trước khi trở về!" + +#: ../src/main.c:173 +msgid "A puzzle game about atoms and molecules" +msgstr "Trò chơi trí tuệ về nguyên tử và phân tử" + +#: ../src/main.c:488 ../atomix.desktop.in.h:1 +msgid "Atomix" +msgstr "Atomix" + +#: ../src/main.c:499 +msgid "Congratulations! You have finished all Atomix levels." +msgstr "Xin chúc mừng! Bạn đã hoàn tất mọi cấp độ của Atomix." + +#: ../src/main.c:509 +msgid "Couldn't find at least one level." +msgstr "Không thể tìm thấy cấp độ nào cả." + +#: ../src/main.c:514 +msgid "Do you want to finish the game?" +msgstr "Bạn có muốn hoàn tất trò chơi không?" + +#. "The branch of mathematics that deals with the relationships among groups of measurements and with the relevance of similarities and differences in those relationships." +#: ../src/main.c:723 ../aisleriot/statistics.glade.h:5 +msgid "Statistics" +msgstr "Thống kê" + +#: ../src/main.c:729 ../gnobots2/statusbar.c:85 ../gnometris/scoreframe.cpp:79 +msgid "Level:" +msgstr "Cấp độ :" + +#: ../src/main.c:730 +msgid "Molecule:" +msgstr "Phân tử :" + +#: ../gnometris/scoreframe.cpp:58 ../gnomine/gnomine.c:441 +msgid "Score:" +msgstr "Điểm:" + +#: ../src/main.c:732 +msgid "Time:" +msgstr "Thời gian:" + +#: ../src/main.c:772 +#, c-format +msgid "Couldn't find file: %s" +msgstr "Không thể tìm tập tin: %s" + +#: ../src/theme-manager.c:135 +msgid "No themes found." +msgstr "Không tìm thấy sắc thái." + +#: ../src/theme-manager.c:193 +#, c-format +msgid "Found theme '%s' in: %s" +msgstr "Tìm thấy sắc thái « %s » trong: %s" + +#: ../atomix.desktop.in.h:2 +msgid "Molecule puzzle game" +msgstr "Trò chơi trí tuệ về phân tử" + +#. #-#-#-#-# NetworkManager.vi.po (NetworkManager HEAD) #-#-#-#-# +#. About item +#: src/mainwin.cpp:555 ../gnome/applet/applet.c:2217 po/silky.glade.h:212 +msgid "_About" +msgstr "_Giới thiệu" + +#: ../gnome-power-preferences.desktop.in.h:1 +msgid "Configure power management" +msgstr "Cấu hình quản lý điện năng" + +#: ../gnome-power-preferences.desktop.in.h:2 +msgid "Power Management" +msgstr "Quản lý Điện năng" + +#: ../gnome-power-manager.schemas.in.h:1 +msgid "If we require a password when resuming from suspend" +msgstr "Nếu cần thiết mật khẩu khi tiếp tục sau khi ngưng" + +#: ../gnome-power-manager.schemas.in.h:2 +msgid "Options are never, critical, charge, always" +msgstr "Tùy chọn là: không bao giờ, tới hạn, nạp, luôn" + +#: ../gnome-power-manager.schemas.in.h:3 +msgid "The action to take when the battery is critically low." +msgstr "Hành động cần làm khi pin yếu tới hạn." + +#: ../gnome-power-manager.schemas.in.h:4 +msgid "The brightness the display is set to on AC" +msgstr "Độ sáng của bộ trình bày khi chạy bằng AC" + +#: ../gnome-power-manager.schemas.in.h:5 +msgid "The brightness the display is set to on battery" +msgstr "Độ sáng của bộ trình bày khi chạy bằng pin" + +#: ../gnome-power-manager.schemas.in.h:6 +msgid "The event for a laptop lid closing" +msgstr "Sự kiện khi máy tính xách tây đóng nắp" + +#: ../gnome-power-manager.schemas.in.h:7 +msgid "The event for a system suspend button press" +msgstr "Sự kiện khi bấm nút ngưng hệ thống" + +#: ../gnome-power-manager.schemas.in.h:8 +msgid "The idle time in seconds before the computer tries to sleep" +msgstr "Thời gian nghỉ theo giây trước khi máy tính cố ngủ" + +#: ../gnome-power-manager.schemas.in.h:9 +msgid "The idle time in seconds before the display tries to sleep" +msgstr "Thời gian nghỉ theo giây trước khi bộ trình bày cố ngủ" + +#: ../gnome-power-manager.schemas.in.h:10 +msgid "The idle time in seconds before the hard disk drives try to sleep" +msgstr "Thời gian nghỉ theo giây trước khi đĩa cứng cố ngủ" + +#: ../gnome-power-manager.schemas.in.h:11 +msgid "" +"The percentage that the powerdevice has to get to be considered \"low enough" +"\" to perform an action." +msgstr "" +"Phần trăm mà thiết bị điện năng cần nhận, để thỏa tiêu chuẩn « đủ yếu » để " +"thực hiện hành động." + +#: ../gnome-power-manager.schemas.in.h:12 +msgid "" +"The percentage that the powerdevice has to get to be considered \"low enough" +"\" to warn the user." +msgstr "" +"Phần trăm mà thiết bị điện năng cần nhận, để thỏa tiêu chuẩn « đủ yếu » để " +"cảnh báo người dùng." + +#: ../gnome-power-manager.schemas.in.h:13 +msgid "The powerdevice action threshold." +msgstr "Ngưỡng hành động thiết bị điện năng." + +#: ../gnome-power-manager.schemas.in.h:14 +msgid "The powerdevice warning threshold." +msgstr "Ngưỡng cảnh báo thiết bị điện năng." + +#: ../gnome-power-manager.schemas.in.h:15 +msgid "The type of sleep (hibernate/suspend) to use automatically." +msgstr "Kiểu ngủ (ngủ động/ngưng) cần dùng tự động." + +#: ../gnome-power-manager.schemas.in.h:16 +msgid "When to show the notification icon" +msgstr "Khi cần hiển thị biểu tượng thông báo" + +#: ../src/eggtray/eggtrayicon.c:117 libexif/exif-tag.c:118 +#: ../gnome/applet/eggtrayicon.c:128 +msgid "Orientation" +msgstr "Hướng" + +#: ../src/eggtray/eggtrayicon.c:118 ../gnome/applet/eggtrayicon.c:129 +msgid "The orientation of the tray." +msgstr "Hướng khay." + +#: ../src/gpm-common.c:133 ../dirdiff.py:571 +#, c-format, python-format +msgid "%i minute" +msgid_plural "%i minute" +msgstr[0] "%i phút" + +#: ../src/gpm-common.c:144 ../dirdiff.py:572 +#, c-format, python-format +msgid "%i hour" +msgid_plural "%i hour" +msgstr[0] "%i giờ" + +# Variable: don't translate / Biến: đừng dịch +#: ../src/gpm-common.c:150 +#, c-format +msgid "%i %s, %i %s" +msgstr "%i %s, %i %s" + +#: ../src/crontab.py:246 ../bin/ical-dump.c:85 +msgid "hour" +msgid_plural "hour" +msgstr[0] "giờ" + +#: ../src/crontab.py:244 ../bin/ical-dump.c:83 +msgid "minute" +msgid_plural "minute" +msgstr[0] "phút" + +#. common descriptions of this program +#: ../src/gpm-common.h:33 ../src/gpm-main.c:685 +msgid "GNOME Power Manager" +msgstr "Bộ Quản lý Điện năng GNOME" + +#: ../src/gpm-common.h:34 +msgid "Power Manager for the GNOME desktop" +msgstr "Ứng dụng quản lý điện năng cho môi trường Gnome" + +#: ../src/gpm-console.c:306 ../src/gpm-main.c:668 +msgid "Do not daemonize" +msgstr "Đừng chạy trong nền" + +#: ../src/gpm-console.c:308 ../src/gpm-main.c:670 ../src/gpm-prefs.c:562 +msgid "Show extra debugging information" +msgstr "Hiển thị thông tin gỡ lỗi thêm" + +#: ../src/gpm-main.c:353 ../src/gpm-main.c:372 +#, c-format +msgid "" +"You have approximately %s of remaining battery life (%i%%). Plug in " +"your AC Adapter to avoid losing data." +msgstr "" +"Bạn có xấp xỉ %s thời gian pin còn lại (%i%%). Hãy cầm phít bộ tiếp " +"hợp AC để tránh mất dữ liệu." + +#: ../src/gpm-main.c:357 +msgid "Battery Critically Low" +msgstr "Pin yếu tới hạn" + +#: ../src/gpm-main.c:375 +msgid "Battery Low" +msgstr "Pin yếu" + +#: ../src/gpm-main.c:417 +msgid "AC Power Unplugged" +msgstr "Điện năng AC chưa kết nối" + +#: ../src/gpm-main.c:418 +msgid "The AC Power has been unplugged. The system is now using battery power." +msgstr "Điện năng AC bị tháo nút ra. Hệ thống đang chạy bằng pin." + +#: ../src/gpm-main.c:494 +msgid "Battery Charged" +msgstr "Pin đầy" + +#: ../src/gpm-main.c:494 +msgid "Your battery is now fully charged" +msgstr "Pin đã được tái sạc đầy." + +#: ../src/gpm-notification.c:216 +msgid "charging" +msgstr "đang sạc" + +#: ../src/gpm-notification.c:218 +msgid "discharging" +msgstr "đang phóng ra" + +#: ../src/gpm-notification.c:221 +msgid "charged" +msgstr "được sạc" + +#: ../src/gpm-notification.c:236 +msgid "until charged" +msgstr "đến khi được sạc" + +#: ../src/gpm-notification.c:239 +msgid "until empty" +msgstr "đến khi rỗng" + +#: ../src/gpm-notification.c:306 +msgid "Computer is running on battery power\n" +msgstr "Hệ thống đang chạy bằng pin\n" + +#: ../src/gpm-notification.c:308 +msgid "Computer is running on AC power\n" +msgstr "Hệ thống đang chạy bằng năng lượng xoay chiều (AC)\n" + +#: ../src/gpm-notification.c:338 +msgid "Licensed under the GNU General Public License Version 2" +msgstr "" +"Được phát hành với điều kiện của Quyền Công Chung GNU (GPL) phiên bản 2" + +#: ../src/gpm-notification.c:339 +msgid "" +"GNOME Power Manager is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU General Public License\n" +"as published by the Free Software Foundation; either version 2\n" +"of the License, or (at your option) any later version." +msgstr "" +"Chương trình này là phần mềm tự do nên bạn có thể phân phối lại nó và sửa " +"đổi nó với điều kiện của Quyền Công Chung GNU (GPL) như do Tổ chức Phần mềm " +"Tự do sản xuất, hoặc phiên bản 2 của Quyền hoặc (tùy chọn) bất cứ phiên bản " +"sau nào." + +#: ../src/gpm-notification.c:343 +msgid "" +"GNOME Power Manager is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details." +msgstr "" +"Bộ Quản lý Điện năng Gnome được phân phối vì mong muốn nó hữu ích\n" +"nhưng KHÔNG CÓ SỰ BẢO ĐẢM NÀO, thậm chí không có\n" +"TÍNH THƯƠNG MẠI hay CHO MỘT MỤC ĐÍCH ĐẶC BIỆT NÀO CẢ.\n" +"Hãy xem Quyền Công Chung GNU để tìm chi tiết." + +#: ../src/gpm-notification.c:347 +msgid "" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n" +"02110-1301, USA." +msgstr "" +"Bện nên đã nhận một bản sao của Quyền Công Chung GNU\n" +"cũng với chương trình này; nếu không thì hãy viết cho\n" +"Tổ chức Phần mềm Tự do:\n" +"Free Software Foundation, Inc.,\n" +"51 Franklin Street, Fifth Floor,\n" +"Boston, MA 02110-1301, USA. (Mỹ)" + +#: ../src/gpm-notification.c:484 +msgid "_Suspend" +msgstr "_Ngưng" + +#: ../src/gpm-notification.c:489 +msgid "Hi_bernate" +msgstr "Ngủ _đông" + +#: ../src/gpm-notification.c:494 ../data/Deskbar_Applet.xml.h:2 +#: ../app/actions/dialogs-actions.c:190 ../src/login.c:917 +#: ../src/mlview-app.cc:310 po/silky.glade.h:218 +msgid "_Preferences" +msgstr "Tù_y thích" + +#: ../src/gpm-prefs.c:44 src/gbiff2.strings:140 +msgid "Suspend" +msgstr "Ngưng" + +#: ../src/gpm-prefs.c:45 +msgid "Shutdown" +msgstr "Tắt máy" + +#: ../src/gpm-prefs.c:46 +msgid "Hibernate" +msgstr "Ngủ đông" + +#: ../src/gpm-prefs.c:47 +msgid "Do nothing" +msgstr "Đừng làm gì" + +#: ../src/gpm-prefs.c:505 +msgid "Configuration" +msgstr "Cấu hình" + +#: ../src/gpm-prefs.c:575 +msgid "GNOME Power Preferences" +msgstr "Tùy thích cho ĐIện năng Gnome" + +#: ../src/gpm-prefs.glade.h:1 +msgid "Actions" +msgstr "Hành động" + +#: ../src/gnome-schedule.glade.h:4 ../gncal/calendar-editor.glade.h:5 +#: ../glade/straw.glade.h:9 +msgid "General" +msgstr "Chung" + +#: ../src/gpm-prefs.glade.h:3 ../src/drivel.glade.h:10 +msgid "Notification Area" +msgstr "Vùng thông báo" + +#: ../src/gpm-prefs.glade.h:4 +msgid "Other Options" +msgstr "Tùy chọn khác" + +#: ../src/gpm-prefs.glade.h:5 +msgid "Running on AC Adapter" +msgstr "Đang chạy bằng bộ kết hợp AC" + +#: ../src/gpm-prefs.glade.h:6 +msgid "Running on Batteries" +msgstr "Đang chạy bằng pin" + +#: ../src/gpm-prefs.glade.h:7 +msgid "Estimated 16 minutes" +msgstr "Ứơc tính 16 phút" + +#: ../src/gpm-prefs.glade.h:8 +msgid "Estimated 2 hours 6 minutes" +msgstr "Ứơc tính 2 giờ 6 phút" + +#: ../src/gpm-prefs.glade.h:9 ../src/gnome-schedule.glade.h:14 +#: ../baobab.glade.h:2 ../plug-ins/metadata/interface.c:405 +msgid "Advanced" +msgstr "Cấp cao" + +#: ../src/gpm-prefs.glade.h:10 +msgid "Ba_ttery is critical when below:" +msgstr "_Pin yếu tới hạn khi dưới:" + +#: ../src/gpm-prefs.glade.h:11 +msgid "Only display when battery life is _critical" +msgstr "Hiển thị chỉ khi thời gian pin tới _hạn" + +#: ../src/gpm-prefs.glade.h:12 +msgid "Only display when charging or _discharging" +msgstr "Hiển thị chỉ khi sạc hay phóng _ra" + +#: ../src/gpm-prefs.glade.h:14 +msgid "Power Preferences" +msgstr "Tùy thích Điện năng" + +#: ../src/gpm-prefs.glade.h:15 +msgid "Put _computer to sleep after:" +msgstr "Cho _máy tính ngủ sau :" + +#: ../src/gpm-prefs.glade.h:16 +msgid "Put _display to sleep after:" +msgstr "Cho bộ trìn_h bày ngủ sau :" + +#: ../src/gpm-prefs.glade.h:17 +msgid "Put c_omputer to sleep after:" +msgstr "Cho má_y tính ngủ sau :" + +#: ../src/gpm-prefs.glade.h:18 +msgid "Put dis_play to sleep after:" +msgstr "Cho bộ trình _bày ngủ sau :" + +#: ../src/gpm-prefs.glade.h:19 +msgid "Require password when returning from sleep" +msgstr "Cần thiết mật khẩu khi mới chạy sau khi ngủ" + +#: ../src/gpm-prefs.glade.h:20 +msgid "Set display _brightness:" +msgstr "Đặt độ _sáng cho bộ trình bày:" + +#: ../src/gpm-prefs.glade.h:21 +msgid "Set display b_rightness:" +msgstr "Đặt _độ sáng cho bộ trình bày:" + +#: ../src/gpm-prefs.glade.h:22 +#: ../schemas/apps_gnome_settings_daemon_keybindings.schemas.in.h:29 +msgid "Sleep" +msgstr "Ngủ" + +#: ../src/gpm-prefs.glade.h:23 +msgid "When _battery power critical:" +msgstr "Khi nạp _pin tới hạn:" + +#: ../src/gpm-prefs.glade.h:24 +msgid "When _suspend button pressed:" +msgstr "Khi bấm nút _ngưng:" + +#: ../src/gpm-prefs.glade.h:25 +msgid "When laptop li_d is closed:" +msgstr "Khi đóng _nắp máy tính xách tây:" + +#: ../src/gpm-prefs.glade.h:26 +msgid "_Always display icon" +msgstr "_Luôn hiển thị biểu tượng" + +#: ../src/gpm-prefs.glade.h:27 +msgid "_Battery is low when below:" +msgstr "_Pin yếu khi dưới:" + +#: ../src/gpm-prefs.glade.h:28 +msgid "_Computer sleep type:" +msgstr "_Kiểu ngủ máy tính:" + +#: ../src/gpm-prefs.glade.h:29 +msgid "_Never display icon" +msgstr "_Chưa bao giờ hiển thị biểu tượng" + +#: ../src/gpm-sysdev.c:57 +msgid "Laptop battery" +msgstr "Pin máy tính xách tây" + +#: ../src/gpm-sysdev.c:59 ../sheets/cisconetwork.sheet.in.h:87 +msgid "UPS" +msgstr "UPS" + +#: ../src/gpm-sysdev.c:61 +msgid "Wireless mouse" +msgstr "Chuột vô tuyến" + +#: ../src/gpm-sysdev.c:63 +msgid "Wireless keyboard" +msgstr "Bàn phím vô tuyến" + +#: ../src/gpm-sysdev.c:65 +msgid "Misc PDA" +msgstr "Máy tính cầm tây lặt vặt" + +#: ../data/Deskbar_Applet.server.in.in.h:1 +msgid "An all-in-one action bar" +msgstr "Thanh hành động hoàn thành" + +#: ../data/Deskbar_Applet.server.in.in.h:2 ../deskbar/about.py:23 +msgid "Deskbar" +msgstr "Deskbar" + +#: ../data/prefs-dialog.glade.h:1 +msgid "Keyboard Shortcut" +msgstr "Phím tắt" + +#: ../data/prefs-dialog.glade.h:2 +#: ../extensions/extensions-manager-ui/extensions-manager-ui.glade.h:1 +msgid "Loaded Extensions" +msgstr "Phần mở rộng đã tải" + +#: ../data/prefs-dialog.glade.h:3 +msgid "Width" +msgstr "Rộng" + +#: ../data/prefs-dialog.glade.h:4 +msgid "" +"Note: Drag and drop an extension to change its order." +msgstr "" +"Ghi chú : Hãy kéo và thả phần mở rộng nào để thay đổi thứ " +"tự." + +#: ../data/prefs-dialog.glade.h:5 +msgid "Deskbar Preferences" +msgstr "Tùy thích Deskbar" + +#: ../data/prefs-dialog.glade.h:6 +msgid "Fixed _width:" +msgstr "_Rộng có định:" + +#: ../data/prefs-dialog.glade.h:7 +msgid "Use _all available space" +msgstr "Dùng toàn _chỗ sẵn sàng" + +#: ../data/prefs-dialog.glade.h:8 +msgid "Use the _keyboard shortcut:" +msgstr "Dùng _phím tắt:" + +#: ../data/smart-bookmarks.glade.h:1 +msgid "" +"Note: If that shortcut is a single letter (like t) " +"you can also just type \"something\" and then press Ctrl-t in " +"the deskbar." +msgstr "" +"Ghi chú : Nếu phím tắt là một chữ đơn (như t) thì " +"bạn cũng có thể gõ chỉ « vật gì » rồi bấm Ctrl-t trong thanh." + +#: ../data/smart-bookmarks.glade.h:2 +msgid "" +"Note: To use a shortcut (for example wp) to search " +"for something, type \"wp something\" in the deskbar." +msgstr "" +"Ghi chú : Để sử dụng phím tắt (v.d. wp) để tìm kiếm " +"vật gì, hãy gõ « wp vật gì » vào thanh.." + +#: ../data/smart-bookmarks.glade.h:3 +msgid "Shortcuts for Bookmarked Searches" +msgstr "Phím tắt cho việc tìm kiếm đã đánh dấu" + +#: ../deskbar/about.py:26 +msgid "An all-in-one action bar." +msgstr "Thanh hành động hoàn thanh." + +#: ../deskbar/about.py:29 +msgid "Deskbar Website" +msgstr "Nơi Mạng Deskbar" + +#: ../deskbar/applet.py:312 +msgid "No History" +msgstr "Không có lược sử" + +#: ../deskbar/handlers/beagle-live.py:19 +msgid "Beagle Live" +msgstr "Beagle tại chỗ" + +#: ../deskbar/handlers/beagle-live.py:20 +msgid "Search all of your documents (using Beagle), as you type" +msgstr "Tìm kiếm trong mọi tài liệu của bạn (bằng Beagle) trong khi gõ" + +#: ../deskbar/handlers/beagle-live.py:47 +#, python-format +msgid "Addressbook entry for %s" +msgstr "Mục nhập sổ địa chỉ cho %s" + +#. translators: First %s is mail sender, second %s is mail subject. +#: ../deskbar/handlers/beagle-live.py:56 +#, python-format +msgid "View email from %s: %s" +msgstr "Xem thừ từ %s: %s" + +#, c-format, python-format +msgid "Open %s" +msgstr "Mở %s" + +#: ../deskbar/handlers/beagle-live.py:69 +#, python-format +msgid "Open news item %s" +msgstr "Mở mục tin tức %s" + +#: ../deskbar/handlers/beagle-live.py:76 +#, python-format +msgid "Open note %s" +msgstr "Mở ghi chép %s" + +#: ../deskbar/handlers/beagle-live.py:82 +#, python-format +msgid "View conversation with %s" +msgstr "Xem cuộc đối thoại với %s" + +#: ../deskbar/handlers/beagle-live.py:88 +#, python-format +msgid "View calendar %s" +msgstr "Xem lịch %s" + +#: dselect/pkgdisplay.cc:89 libexif/exif-entry.c:522 +msgid "?" +msgstr "?" + +#: ../deskbar/handlers/beagle.py:19 +msgid "Beagle" +msgstr "Beagle" + +#: ../deskbar/handlers/beagle.py:20 +msgid "Search all of your documents (using Beagle)" +msgstr "Tìm kiếm trong mọi tài liệu của bạn (bằng Beagle)" + +#: ../deskbar/handlers/beagle.py:33 +#, python-format +msgid "Search for %s using Beagle" +msgstr "Tìm kiếm %s bằng Beagle" + +#: ../deskbar/handlers_browsers.py:30 +#, python-format +msgid "Open History Item %s" +msgstr "Mở mục Lược sử %s" + +#: ../deskbar/handlers_browsers.py:32 +#, python-format +msgid "Open Bookmark %s" +msgstr "Mở Đánh dấu %s" + +#. translators: First %s is the search engine name, second %s is the search term +#: ../deskbar/handlers_browsers.py:67 +#, python-format +msgid "Search %s for %s" +msgstr "Tìm kiếm trong %s có %s" + +#: ../app/widgets/gimpactionview.c:360 ../src/menu-win.cc:269 +msgid "Shortcut" +msgstr "Phím tắt" + +#: ../deskbar/handlers_browsers.py:180 ui/bookmarks.glade.h:3 +msgid "Bookmark Name" +msgstr "Tên Đánh dấu" + +#: ../data/browser.xml.h:51 address_gui.c:1921 address_gui.c:1924 +#: address_gui.c:2938 +#, fuzzy +msgid "Mail" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Thư\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"Lá thư" + +#: ../deskbar/handlers/email_address.py:10 +msgid "Send mail by typing a complete e-mail address" +msgstr "Gởi thư bằng cách gõ địa chỉ thư hoàn thành" + +#: ../deskbar/handlers/email_address.py:26 ../deskbar/handlers/galago.py:27 +#, python-format +msgid "Send Email to %s" +msgstr "Gởi Thư cho %s" + +#: ../deskbar/handlers/epiphany.py:32 ../deskbar/handlers/galeon.py:21 +#: ../deskbar/handlers/mozilla.py:29 ../data/bme.desktop.in.h:3 +msgid "Web Bookmarks" +msgstr "Dấu sách Mạng" + +#: ../deskbar/handlers/epiphany.py:33 ../deskbar/handlers/galeon.py:22 +#: ../deskbar/handlers/mozilla.py:30 +msgid "Open your web bookmarks by name" +msgstr "Mở đánh dấu theo tên" + +#: ../deskbar/handlers/epiphany.py:37 ../deskbar/handlers/galeon.py:26 +msgid "Web History" +msgstr "Lược sử Mạng" + +#: ../deskbar/handlers/epiphany.py:38 ../deskbar/handlers/galeon.py:27 +msgid "Open your web history by name" +msgstr "Mở lược sử Mạng theo tên" + +#: ../deskbar/handlers/epiphany.py:42 ../deskbar/handlers/galeon.py:31 +#: ../deskbar/handlers/mozilla.py:34 +msgid "Web Searches" +msgstr "Tìm kiếm Mạng" + +#: ../deskbar/handlers/epiphany.py:43 ../deskbar/handlers/galeon.py:32 +#: ../deskbar/handlers/mozilla.py:35 +msgid "Search the web via your browser's search settings" +msgstr "Tìm kiếm trên Mạng thông qua thiết lập tìm kiếm của trình duyệt" + +#: ../deskbar/handlers/evolution.py:11 +msgid "You need to enable autocomplete in your mail preferences" +msgstr "Bạn cần phải bật khả năng tự động gõ xong trong tùy thích thư tín" + +#: ../deskbar/handlers/evolution.py:13 +msgid "Autocompletion Needs to be Enabled" +msgstr "Cần phải bật Tự đông Gõ Xong" + +#: ../deskbar/handlers/evolution.py:14 +msgid "" +"We cannot provide e-mail addresses from your address book unless " +"autocompletion is enabled. To do this, from your mail program's menu, " +"choose Edit - Preferences, and then Autocompletion." +msgstr "" +"Không thể cung cấp địa chỉ thư từ sổ địa chỉ nếu bạn chưa bật khả năng tự " +"động gõ xong. Để bật nó, trong trình đơn của trình thư, hãy chọn Hiệu chỉnh " +"→ Tùy thích, rồi Tự động Gõ Xong." + +#: ../deskbar/handlers/evolution.py:19 +msgid "Mail (Address Book)" +msgstr "Thư (Sổ địa chỉ)" + +#: ../deskbar/handlers/evolution.py:20 +msgid "Send mail to your contacts by typing their name or e-mail address" +msgstr "Gởi thư cho liên lạc bằng cách gõ tên hay địa chỉ thư của họ" + +#. translators: First %s is the contact full name, second %s is the email address +#: ../deskbar/handlers/evolution.py:42 +#, python-format +msgid "Send Email to %s (%s)" +msgstr "Gởi thư cho %s (%s)" + +#: ../deskbar/handlers/files.py:14 +msgid "Files and Folders" +msgstr "Tập tin và Thư mục" + +#: ../deskbar/handlers/files.py:15 +msgid "Open your files and folders by name" +msgstr "Mở tập tin và thư mục theo tên" + +#: ../deskbar/handlers/files.py:47 +#, python-format +msgid "Open folder %s" +msgstr "Mở thư mục %s" + +#: ../deskbar/handlers/google-live.py:19 +msgid "" +"You need a Google account to use Google Live. To get one, go to http://api." +"google.com/\n" +"\n" +"When you have created your account, you should recieve a Google API key by " +"mail. Place this key in the file\n" +"\n" +"~/.gnome2/deskbar-applet/Google.key\n" +"\n" +"If you do not receive an API key (or you have lost it) in your account " +"verification mail, then go to www.google.com/accounts and log in. Go to api." +"google.com, click \"Create Account\" and enter your e-mail address and " +"password. Your API key will be re-sent.\n" +"\n" +"Now download the developers kit and extract the GoogleSearch.wsdl file from " +"it. Copy this file to\n" +"\n" +"~/.gnome2/deskbar-applet/GoogleSearch.wsdl" +msgstr "" +"Bạn cần phải có tài khoản Google để sử dụng tính năng Google Live. Để được " +"tài khoản, hãy đi tới \n" +"\n" +"Một khi bạn đã tạo tài khoản mình, bạn nên nhận một khoá API của Google qua " +"thư. Hãy để khoá này vào tập tin\n" +"\n" +"~/.gnome2/deskbar-applet/Google.key\n" +"\n" +"Nếu bạn chưa nhận khoá API trong thư xác định tài khoản (hoặc nó bị mất), " +"hãy thăm và đang nhập. Đi tới , " +"bấm « Tạo Tài khoản » (Create Account) và gõ địa chỉ thư và mật khẩu mình. " +"Sau đó, khoá API sẽ được gởi lại.\n" +"\n" +"Sau đó, bạn hãy tải về bộ công cụ lập trình viên (developer's kit) và rút " +"tập tin ra nó. Sao chép tập tin này vào\n" +"\n" +"~/.gnome2/deskbar-applet/GoogleSearch.wsdl" + +#: ../deskbar/handlers/google-live.py:32 +msgid "Setting Up Google Live" +msgstr "Cách thiết lập Google Live" + +#: ../deskbar/handlers/google-live.py:38 +msgid "You need to install the SOAPpy python module." +msgstr "Bạn cần phải cài đặt mô-đun python SOAPpy." + +#: ../deskbar/handlers/google-live.py:40 +msgid "You need the Google WSDL file." +msgstr "Bạn cần đến tập tin WDSL Google." + +#: ../deskbar/handlers/google-live.py:42 +msgid "You need a Google API key." +msgstr "Bạn cần đến một khoá API Google." + +#: ../deskbar/handlers/google-live.py:48 +msgid "Google Live" +msgstr "Google Live" + +#: ../deskbar/handlers/google-live.py:49 +msgid "Search Google as you type" +msgstr "Tìm kiếm trong Google trong khi gõ" + +#: ../deskbar/handlers/gtkbookmarks.py:12 +msgid "Files and Folders Bookmarks" +msgstr "Đánh dấu Tập tin và Thư mục" + +#: ../deskbar/handlers/gtkbookmarks.py:13 +msgid "Open your files and folders bookmarks by name" +msgstr "Mở đánh dấu của tập tin và thư mục theo tên" + +#: ../deskbar/handlers/gtkbookmarks.py:29 ../deskbar/handlers/volumes.py:45 +#, python-format +msgid "Open location %s" +msgstr "Mở địa điểm %s" + +#: ../deskbar/handlers/pathprograms.py:13 +msgid "Programs (Advanced)" +msgstr "Chương trình (Cấp cao)" + +#: ../deskbar/handlers/pathprograms.py:14 +msgid "Launch any program present in your $PATH" +msgstr "Khởi chạy chương trình nào có trong đường dẫn $PATH của bạn." + +#: ../deskbar/handlers/pathprograms.py:30 +#, python-format +msgid "Execute %s" +msgstr "Thực hiện %s" + +#: ../deskbar/handlers/programs.py:14 +msgid "Programs" +msgstr "Chương trình" + +#: ../deskbar/handlers/programs.py:15 +msgid "Launch a program by its name and/or description" +msgstr "Khời chạy chương trình theo tên hay mô ta" + +#: ../gnopi/gnopi_files/Speech_Settings/speech_settings.glade2.h:31 +#: src/prefsdlg.cpp:75 src/stardict.cpp:1577 +msgid "Dictionary" +msgstr "Từ điển" + +#: ../deskbar/handlers/programs.py:19 +msgid "Look up word definitions in the dictionary" +msgstr "Tìm lời định nghĩa từ trong từ điển" + +#: ../deskbar/handlers/programs.py:22 +msgid "Files and Folders Search" +msgstr "Tìm kiếm Tập tin và Thư mục" + +#: ../deskbar/handlers/programs.py:23 +msgid "Find files and folders by searching for a name pattern" +msgstr "Tìm tập tin và thư mục bằng cách tìm kiếm mẫu tên" + +#. translators: First %s is the programs full name, second is the executable name +#. translators: For example: Launch Text Editor (gedit) +#: ../deskbar/handlers/programs.py:66 +msgid "Launch %s (%s)" +msgstr "Khởi chạy %s (%s)" + +#: ../deskbar/handlers/programs.py:79 +#, python-format +msgid "Lookup %s in dictionary" +msgstr "Tra tìm %s trong từ điển" + +#: ../deskbar/handlers/programs.py:87 +#, python-format +msgid "Search for file names like %s" +msgstr "Tìm kiếm tên tập tin như %s" + +#: ../deskbar/handlers/volumes.py:16 +msgid "Disks and Network Places" +msgstr "Nơi mạng và đĩa" + +#: ../deskbar/handlers/volumes.py:17 +msgid "Open disk drives, shared network places and similar resources by name" +msgstr "Mở ổ đĩa, nơi mạng dùng chung và tài nguyên tương tư theo tên" + +#: ../deskbar/handlers/volumes.py:41 +#, python-format +msgid "Open network place %s" +msgstr "Mở nơi mạng %s" + +#: ../deskbar/handlers/volumes.py:43 +#, python-format +msgid "Open audio disk %s" +msgstr "Mở đĩa âm thanh %s" + +#: ../48x48/emblems/emblem-web.icon.in.h:1 ../data/browser.xml.h:87 +msgid "Web" +msgstr "Mạng" + +#: ../deskbar/handlers/web_address.py:10 +msgid "Open web pages by typing a complete web address" +msgstr "Mở trang Mạng nào bằng cách gõ địa chỉ Mạng hoàn thành" + +#: ../deskbar/handlers/web_address.py:35 +#, python-format +msgid "Open the web page %s" +msgstr "Mở trang Mạng %s" + +#: ../deskbar/handlers/web_address.py:37 +#, python-format +msgid "Open the location %s" +msgstr "Mở địa điểm %s" + +#: ../deskbar/preferences.py:53 +#: ../plugins/spell/gedit-automatic-spell-checker.c:443 +msgid "_More..." +msgstr "Th_êm..." + +#: ../data/gnome-screensaver-preferences.desktop.in.h:1 +msgid "Screensaver" +msgstr "Bộ bảo vệ màn hình" + +#: ../data/gnome-screensaver-preferences.desktop.in.h:2 +msgid "Set your screensaver preferences" +msgstr "Lập các tùy thích cho trình bảo vệ màn hình." + +#: ../data/glade/jamboree.glade.h:2 ../glade/straw.glade.h:3 +msgid " " +msgstr " " + +#: ../data/gnome-screensaver-preferences.glade.h:2 +msgid "_Screensaver" +msgstr "Bộ _bảo vệ màn hình" + +#: ../data/gnome-screensaver-preferences.glade.h:3 +#: ../data/gnome-screensaver-preferences.glade.h:4 +msgid "Screensaver Preferences" +msgstr "Tùy thích Bộ bảo vệ màn hình" + +#: ../data/gnome-screensaver-preferences.glade.h:4 +#: ../data/gnome-screensaver-preferences.glade.h:5 +msgid "_Activate after:" +msgstr "_Hoạt hóa sau :" + +#: ../data/gnome-screensaver-preferences.glade.h:5 +msgid "_Lock screen when active" +msgstr "_Khoá màn hình khi hoạt động" + +#: ../data/gnome-screensaver.directory.in.h:1 +#: ../data/gnome-screensaver.schemas.in.h:7 +msgid "Screensaver themes" +msgstr "Sắc thái của ảnh bảo vệ màn hình" + +#: ../data/gnome-screensaver.directory.in.h:2 +msgid "Screensavers" +msgstr "Ảnh bảo vệ màn hình" + +#: ../data/gnome-screensaver.schemas.in.h:1 +msgid "Allow logout" +msgstr "Cho phép đăng xuất" + +#: ../data/gnome-screensaver.schemas.in.h:2 +msgid "Allow monitor power management" +msgstr "Cho phép quản lý điện năng của bộ trình bày" + +#: ../data/gnome-screensaver.schemas.in.h:3 +msgid "Allow user switching" +msgstr "Cho phép chuyển đổi người dùng" + +#: ../data/gnome-screensaver.schemas.in.h:4 +msgid "Lock on activation" +msgstr "Khoá khi mới hoạt động" + +#: ../data/gnome-screensaver.schemas.in.h:5 +msgid "Logout command" +msgstr "Lệnh đăng xuất" + +#: ../data/gnome-screensaver.schemas.in.h:6 +msgid "Screensaver selection mode" +msgstr "Chế độ lựa chọn ảnh bảo vệ màn hình" + +#: ../data/gnome-screensaver.schemas.in.h:8 +msgid "Set this to TRUE to allow the screensaver to power down the monitor." +msgstr "" +"Đặt giá trị này là TRUE (đúng) để cho phép bộ bảo vậ màn hình tắt điện của " +"thiết bị hiển thị." + +#: ../data/gnome-screensaver.schemas.in.h:9 +msgid "Set this to TRUE to lock the screen when the screensaver goes active." +msgstr "" +"Đặt giá trị này là TRUE (đúng) để khoá màn hình khi bộ bảo vệ màn hình mới " +"hoạt động." + +#: ../data/gnome-screensaver.schemas.in.h:10 +msgid "" +"Set this to TRUE to offer an option in the unlock dialog to switch to a " +"different user account." +msgstr "" +"Đặt giá trị này là TRUE (đúng) để cung cấp trong hộp thoại bỏ khoá tùy chọn " +"chuyển đổi sang tài khoản người dùng khác." + +#: ../data/gnome-screensaver.schemas.in.h:11 +msgid "" +"Set this to TRUE to offer an option in unlock dialog to logging out after a " +"delay. The Delay is specified in the \"logout_delay\" key." +msgstr "" +"Đặt giá trị này là TRUE (đúng) để cung cấp trong hộp thoại bỏ khoá tùy chọn " +"đăng xuất sau khi trễ. Sự trễ được ghi rõ trong khoá « logout_delay » (sự " +"trễ đăng xuất)." + +#: ../data/gnome-screensaver.schemas.in.h:12 +msgid "" +"The command to invoke when the logout button is clicked. This command should " +"simply log the user out without any interaction. This key has effect only if " +"the \"logout_enable\" key is set to TRUE." +msgstr "" +"Lệnh cần chạy khi cái nút đăng xuất được bấm. Lệnh này nên đơn giản đăng " +"xuất người dùng, không tương tác gì." + +#: ../data/gnome-screensaver.schemas.in.h:13 +msgid "" +"The number of minutes after screensaver activation before locking the screen." +msgstr "" +"Số phút sau khi bộ bảo vệ màn hình mới hoạt động, trước khi khoá màn hình." + +#: ../data/gnome-screensaver.schemas.in.h:14 +msgid "" +"The number of minutes after the screensaver activation before a logout " +"option will appear in unlock dialog. This key has effect only if the " +"\"logout_enable\" key is set to TRUE." +msgstr "" +"Số phút sau khi bộ bảo vệ màn hình mới hoạt động, trước khi tùy chọn đăng " +"xuất được hiển thị trong hộp thoại bỏ khoá. Khoá này có tác động chỉ nếu " +"khoá « logout_enable » (bật đăng xuất) được đặt là TRUE (đúng)." + +#: ../data/gnome-screensaver.schemas.in.h:15 +msgid "" +"The number of minutes after the screensaver activation until the monitor " +"goes into standby power mode." +msgstr "" +"Số phút sau khi bộ bảo vệ màn hình mới hoạt động, trước khi thiết bị hiển " +"thị vào chế độ chờ." + +#: ../data/gnome-screensaver.schemas.in.h:16 +msgid "" +"The number of minutes after the screensaver activation until the monitor " +"goes into suspend power mode." +msgstr "" +"Số phút sau khi bộ bảo vệ màn hình mới hoạt động, trước khi thiết bị hiển " +"thị vào chế độ ngưng điện năng." + +#: ../data/gnome-screensaver.schemas.in.h:17 +msgid "" +"The number of minutes after the screensaver activation until the monitor " +"powers off." +msgstr "" +"Số phút sau khi bộ bảo vệ màn hình mới hoạt động, trước khi thiết bị hiển " +"thị tắt điện." + +#: ../data/gnome-screensaver.schemas.in.h:18 +msgid "The number of minutes of idle time before activating the screensaver." +msgstr "Số phút nghỉ trước khi hoạt hóa bộ bảo vệ màn hình." + +#: ../data/gnome-screensaver.schemas.in.h:19 +msgid "The number of minutes to run before changing the screensaver theme." +msgstr "" +"Bao nhiêu phút cần chạy trước khi thay đổi sắc thái ảnh bảo vệ màn hình." + +#: ../data/gnome-screensaver.schemas.in.h:20 +msgid "" +"The selection mode used by screensaver. May be \"disabled\" to disable " +"screensaver activation, \"blank-only\" to enable the screensaver without " +"using any theme on activation, \"single\" to enable screensaver using only " +"one theme on activation (specified in \"themes\" key), and \"random\" to " +"enable the screensaver using a random theme on activation." +msgstr "" +"Chế độ lựa chọn được dùng bởi bộ bảo vệ màn hình. Chế độ có thể:\n" +" • disabled (bị tắt) để tắt khả năng hoạt hóa bộ bảo vệ màn hình\n" +" • blank-only (chỉ trắng) để bật chạy bộ bảo vệ màn hình mà không dùng sắc " +"thái nào khi mới hoạt động\n" +" • single (đơn) để bật chạy bộ bảo vệ màn hình mà dùng chỉ một sắc thái khi " +"mới hoạt động thôi (được ghi rõ trong khoá « themes » (sắc thái)\n" +" • random (ngẫu nhiên) để bật chạy bộ bảo vệ màn hình mà dùng sắc thái ngẫu " +"nhiên khi mới hoạt động." + +#: ../data/gnome-screensaver.schemas.in.h:21 +msgid "" +"This key specifies the list of themes to be used by the screensaver. It's " +"ignored when \"mode\" key is \"disabled\" or \"blank-only\", should provide " +"the theme name when \"mode\" is \"single\", and should provide a list of " +"themes when \"mode\" is \"random\"." +msgstr "" +"Khoá này ghi rõ danh sách các sắc thái cho bộ bảo vệ màn hình dùng. Nó\n" +" • bị bỏ qua khi khoá « mode » (chế độ) bị tắt (« disabled ») hay chỉ trắng " +"(« blank only »)\n" +" • nên cung cấp tên sắc thái khi « mode » (chế độ) là đơn (« single »)\n" +" • nên cung cấp danh sách các sắc thái khi « mode » (chế độ) là ngẫu nhiên " +"(« random »)." + +#: ../data/gnome-screensaver.schemas.in.h:22 +msgid "Time before activation" +msgstr "Thời gian trước khi hoạt hóa" + +#: ../data/gnome-screensaver.schemas.in.h:23 +msgid "Time before locking" +msgstr "Thời gian trước khi khoá" + +#: ../data/gnome-screensaver.schemas.in.h:24 +msgid "Time before logout option" +msgstr "Thời gian trước khi nhận tùy chọn đăng xuất" + +#: ../data/gnome-screensaver.schemas.in.h:25 +msgid "Time before power off" +msgstr "Thời gian trước khi tắt điện" + +#: ../data/gnome-screensaver.schemas.in.h:26 +msgid "Time before standby" +msgstr "Thời gian trước khi trạng thái chờ" + +#: ../data/gnome-screensaver.schemas.in.h:27 +msgid "Time before suspend" +msgstr "Thời gian trước khi ngưng" + +#: ../data/gnome-screensaver.schemas.in.h:28 +msgid "Time before theme change" +msgstr "Thời gian trước khi thay đổi sắc thái" + +#: ../savers/cosmos-slideshow.desktop.in.in.h:1 +#: ../savers/cosmos-slideshow.xml.in.h:1 +msgid "Cosmos" +msgstr "Vũ trụ" + +#: ../savers/cosmos-slideshow.desktop.in.in.h:2 +msgid "Display a slideshow of pictures of the cosmos" +msgstr "Hiển thị trình diễn các ảnh của vũ trụ" + +#: ../savers/personal-slideshow.desktop.in.h:1 +msgid "Display a slideshow from your Pictures folder" +msgstr "Hiển thị trình diễn các ảnh từ thư mục Ảnh (Pictures) của bạn" + +#: ../savers/personal-slideshow.xml.h:1 +msgid "Pictures folder" +msgstr "Thư mục Ảnh" + +#: ../savers/popsquares.desktop.in.h:1 +msgid "A pop-art-ish grid of pulsing colors." +msgstr "Vẽ lưới các màu đập kiểu dáng nghệ thuật phổ biến." + +#: ../savers/popsquares.desktop.in.h:2 ../savers/popsquares.xml.h:1 +msgid "Pop art squares" +msgstr "Vuông nghệ thuật phổ biến" + +#: ../savers/floaters.c:1164 +msgid "show paths that images follow" +msgstr "hiển thị các đường dẫn mà ảnh theo" + +#: ../savers/floaters.c:1171 +msgid "occasionally rotate images as they move" +msgstr "thỉng thoảng quay ảnh trong khi di chuyển" + +#: ../savers/floaters.c:1178 +msgid "print out frame rate and other statistics" +msgstr "in ra tốc độ khung (frame rate) và thống kê khác" + +#: ../savers/floaters.c:1186 +msgid "the maximum number of images to keep on screen" +msgstr "số ảnh tối đa cần giữ trên màn hình" + +#: ../savers/floaters.c:1190 +msgid "N" +msgstr "N" + +#: ../savers/floaters.c:1197 +msgid "the source image to use" +msgstr "ảnh nguồn cần dùng" + +#: ../savers/floaters.c:1204 +msgid "the initial size and position of window" +msgstr "kích cỡ và vị trí ban đầu của cửa sổ" + +#: ../savers/floaters.c:1209 ../src/gnomeicu.c:543 +msgid "WIDTHxHEIGHT+X+Y" +msgstr "RỘNGxCAO+X+Y" + +#: ../savers/floaters.c:1229 +msgid "image - floats images around the screen" +msgstr "image - làm nổi ảnh ở chung quanh màn hình" + +#: ../savers/floaters.c:1239 +#, c-format +msgid "%s. See --help for usage information.\n" +msgstr "" +"%s: Hãy chạy lệnh « --help » (trợ giúp) để xem thông tin về cách sử dụng.\n" + +#: ../savers/floaters.c:1248 +msgid "You must specify one image. See --help for usage information.\n" +msgstr "" +"Bạn phải ghi rõ một ảnh. Hãy chạy lệnh « --help » (trợ giúp) để xem thông " +"tin về cách sử dụng.\n" + +#: ../savers/slideshow.c:47 ../savers/slideshow.c:63 +msgid "Location to get images from" +msgstr "Địa điểm nơi cần lấy ảnh" + +#: ../msearch/medusa-command-line-search.c:156 ../savers/slideshow.c:63 +msgid "PATH" +msgstr "ĐƯỜNG DẪN" + +#. "Any program that is designed to perform a certain set of housekeeping tasks related to computer operation, such as the maintenance of files." +#: ../src/cut-n-paste/fusa-display.c:119 ../src/cut-n-paste/fusa-user.c:150 +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:22 +msgid "Manager" +msgstr "Bộ quản lý" + +#: ../src/cut-n-paste/fusa-display.c:120 ../src/fusa-display.c:120 +msgid "The manager which owns this object." +msgstr "Quản trị sở hữu đối tượng này." + +#: ../src/cut-n-paste/fusa-display.c:128 ../src/fusa-display.c:128 +msgid "The name of the X11 display this object refers to." +msgstr "Tên của bộ trình bày X11 đến đó đối tượng này tham chiếu." + +#: ../src/cut-n-paste/fusa-display.c:135 ../src/fusa-display.c:135 +msgid "The user currently logged in on this virtual terminal." +msgstr "Người dùng mà hiện thời đã đăng nhập vào thiết bị cuối ảo này." + +#: ../src/cut-n-paste/fusa-display.c:141 serial.c:446 +#: ../src/fusa-display.c:141 +msgid "Console" +msgstr "Bàn giao tiếp" + +#: ../src/cut-n-paste/fusa-display.c:142 ../src/fusa-display.c:142 +msgid "The number of the virtual console this display can be found on, or %-1." +msgstr "" +"Số hiệu của bàn giao tiếp ảo trên đó bộ trình bày có thể được tìm, hoặc %-1." + +#: ../src/cut-n-paste/fusa-display.c:148 ../src/fusa-display.c:148 +msgid "Nested" +msgstr "Lồng nhau" + +#: ../src/cut-n-paste/fusa-display.c:149 ../src/fusa-display.c:149 +msgid "Whether or not this display is a windowed (Xnest) display." +msgstr "Có nên bộ trình bày bày là bộ trình bày có cửa sổ (Xnest) hay không." + +#: ../src/cut-n-paste/fusa-manager.c:1263 ../src/fusa-manager.c:1263 +#: ../src/cut-n-paste/fusa-manager.c:1264 +msgid "The display manager could not be contacted for unknown reasons." +msgstr "Không thể liên lạc với bộ quản lý trình bày, không biết sao." + +#: ../src/cut-n-paste/fusa-manager.c:1270 ../src/fusa-manager.c:1270 +#: ../src/cut-n-paste/fusa-manager.c:1271 +msgid "The display manager is not running or too old." +msgstr "Bộ quản lý trình bày không đang chạy hoặc nó quá cũ." + +#: ../src/cut-n-paste/fusa-manager.c:1273 ../src/fusa-manager.c:1273 +#: ../src/cut-n-paste/fusa-manager.c:1274 +msgid "The configured limit of flexible servers has been reached." +msgstr "Mới tới giới hạn đã cấu hình của số trình phục vụ dẻo." + +#: ../src/cut-n-paste/fusa-manager.c:1276 ../src/fusa-manager.c:1276 +#: ../src/cut-n-paste/fusa-manager.c:1277 +msgid "There was an unknown error starting X." +msgstr "Gặp lỗi không xác định khi khởi chạy X." + +#: ../src/cut-n-paste/fusa-manager.c:1279 ../src/fusa-manager.c:1279 +#: ../src/cut-n-paste/fusa-manager.c:1280 +msgid "The X server failed to finish starting." +msgstr "Trình phục vụ X không khởi chạy hoàn thành." + +#: ../src/cut-n-paste/fusa-manager.c:1282 ../src/fusa-manager.c:1282 +#: ../src/cut-n-paste/fusa-manager.c:1283 +msgid "There are too many X sessions running." +msgstr "Có quá nhiều phiên đang chạy X." + +#: ../src/cut-n-paste/fusa-manager.c:1285 ../src/fusa-manager.c:1285 +#: ../src/cut-n-paste/fusa-manager.c:1286 +msgid "The nested X server (Xnest) cannot connect to your current X server." +msgstr "" +"Trình phục vụ X lồng nhau (Xnest) không thể kết nối đến trình phục vụ X hiện " +"thời của bạn." + +#: ../src/cut-n-paste/fusa-manager.c:1288 ../src/fusa-manager.c:1288 +#: ../src/cut-n-paste/fusa-manager.c:1289 +msgid "The X server in the GDM configuration could not be found." +msgstr "Không tìm thấy trình phục vụ X trong cấu hình GDM." + +#: ../src/cut-n-paste/fusa-manager.c:1292 +msgid "" +"Trying to set an unknown logout action, or trying to set a logout action " +"which is not available." +msgstr "" +"Đã cố đặt hành động đăng xuất lạ, hoặc một hành động đăng xuất không sẵn " +"sàng." + +#: ../src/cut-n-paste/fusa-manager.c:1295 +msgid "Virtual terminals not supported." +msgstr "Không hỗ trợ thiết bị cuối ảo." + +#: ../src/cut-n-paste/fusa-manager.c:1297 ../src/fusa-manager.c:1297 +#: ../src/cut-n-paste/fusa-manager.c:1298 +msgid "Invalid virtual terminal number." +msgstr "Số thiết bị cuối ảo không hợp lệ." + +#: ../src/cut-n-paste/fusa-manager.c:1301 +msgid "Trying to update an unsupported configuration key." +msgstr "Đã cố cập nhật một khoá cấu hình không được hỗ trợ." + +#: ../src/cut-n-paste/fusa-manager.c:1303 ../src/fusa-manager.c:1303 +#: ../src/cut-n-paste/fusa-manager.c:1304 +msgid "~/.Xauthority file badly configured or missing." +msgstr "Tập tin « ~/.Xauthority » có cấu hình sai, hoặc thiếu nó." + +#: ../src/cut-n-paste/fusa-manager.c:1306 ../src/fusa-manager.c:1306 +#: ../src/cut-n-paste/fusa-manager.c:1307 +msgid "Too many messages were sent to the display manager, and it hung up." +msgstr "" +"Quá nhiều thông điệp đã được gởi cho bộ quản lý trình bày nên nó ngắt kết " +"nối." + +#: ../src/cut-n-paste/fusa-manager.c:1310 ../src/fusa-manager.c:1310 +#: ../src/cut-n-paste/fusa-manager.c:1311 +msgid "The display manager sent an unknown error message." +msgstr "Bộ quản lý trình bày đã gởi một thông điệp lỗi không xác định." + +#: ../src/cut-n-paste/fusa-user-menu-item.c:153 +msgid "The user this menu item represents." +msgstr "Người dùng mà mục trình đơn này tiêu biểu." + +#: ../src/cut-n-paste/fusa-user-menu-item.c:160 ../src/splashwindow.c:201 +msgid "Icon Size" +msgstr "Cỡ biểu tượng" + +#: ../src/cut-n-paste/fusa-user-menu-item.c:161 +msgid "The size of the icon to use." +msgstr "Kích cỡ của biểu tượng cần dùng." + +#: ../src/cut-n-paste/fusa-user.c:151 ../src/fusa-user.c:151 +msgid "The user manager object this user is controlled by." +msgstr "Đối tượng bộ quản lý người dùng có điều khiển người dùng này." + +#: ../src/cut-n-paste/fusa-utils.c:80 src/DialogMain.cc:60 +#: ../src/fusa-utils.c:80 +msgid "Show Details" +msgstr "Hiện chi tiết" + +#. markup +#: ../src/cut-n-paste/gdmcomm.c:413 ../src/gdmcomm.c:413 +msgid "GDM (The GNOME Display Manager) is not running." +msgstr "GDM (Bộ quản lý trình bày Gnome) không đang chạy." + +#: ../src/cut-n-paste/gdmcomm.c:416 ../src/gdmcomm.c:416 +msgid "" +"You might in fact be using a different display manager, such as KDM (KDE " +"Display Manager) or xdm." +msgstr "" +"Có lẽ bạn thật sự có sử dụng một bộ quản lý trình bày khác, như KDM (Bộ quản " +"lý trình bày KDE) hoặc xdm." + +#: ../src/cut-n-paste/gdmcomm.c:419 ../src/gdmcomm.c:419 +msgid "" +"If you still wish to use this feature, either start GDM yourself or ask your " +"system administrator to start GDM." +msgstr "" +"Nếu bạn vẫn còn muốn sử dụng tính năng này, hãy hoặc tự khởi chạy GDM, hoặc " +"xin quản trị hệ thống làm như thế." + +#. markup +#: ../src/cut-n-paste/gdmcomm.c:441 ../src/gdmcomm.c:441 +msgid "Cannot communicate with GDM (The GNOME Display Manager)" +msgstr "Không thể liên lạc với GDM (Bộ quản lý trình bày Gnome)." + +#: ../src/cut-n-paste/gdmcomm.c:444 ../src/gdmcomm.c:444 +msgid "Perhaps you have an old version of GDM running." +msgstr "Có lẽ bạn đang chạy một phiên bản GDM cũ." + +#: ../src/cut-n-paste/gdmcomm.c:463 ../src/cut-n-paste/gdmcomm.c:466 +#: ../src/gdmcomm.c:463 ../src/gdmcomm.c:466 +msgid "Cannot communicate with gdm, perhaps you have an old version running." +msgstr "Không thể liên lạc với GDM, có lẽ bạn đang chạy một phiên bản GDM cũ." + +#: ../src/cut-n-paste/gdmcomm.c:469 ../src/gdmcomm.c:469 +msgid "The allowed limit of flexible X servers reached." +msgstr "Mới tới giới hạn số trình phục vụ X dẻo được phép." + +#: ../src/cut-n-paste/gdmcomm.c:471 ../src/gdmcomm.c:471 +msgid "There were errors trying to start the X server." +msgstr "Gặp lỗi khi cố khởi chạy trình phục vụ X." + +#: ../src/cut-n-paste/gdmcomm.c:473 ../src/gdmcomm.c:473 +msgid "The X server failed. Perhaps it is not configured well." +msgstr "Trình phục vụ X thất bại. Có lẽ nó có cấu hình sai." + +#: ../src/cut-n-paste/gdmcomm.c:476 ../src/gdmcomm.c:476 +msgid "Too many X sessions running." +msgstr "Quá nhiều phiên X đang chạy." + +#: ../src/cut-n-paste/gdmcomm.c:478 ../src/gdmcomm.c:478 +msgid "" +"The nested X server (Xnest) cannot connect to your current X server. You " +"may be missing an X authorization file." +msgstr "" +"Trình phục vụ X lồng nhau (Xnest) không thể kết nối đến trình phục vụ X hiện " +"thời của bạn. Có lẽ bạn thiếu một tập tin cấp quyền X." + +#: ../src/cut-n-paste/gdmcomm.c:483 ../src/gdmcomm.c:483 +msgid "" +"The nested X server (Xnest) is not available, or gdm is badly configured.\n" +"Please install the Xnest package in order to use the nested login." +msgstr "" +"Trình phục vụ X lồng nhau (Xnest) không sẵn sàng, hoặc GDM có cấu hình sai.\n" +"Bạn hãy cài đặt gói Xnest, để sử dụng khả năng đăng nhập lồng nhau." + +#: ../src/cut-n-paste/gdmcomm.c:488 ../src/gdmcomm.c:488 +msgid "" +"The X server is not available, it is likely that gdm is badly configured." +msgstr "" +"Trình phục vụ X lồng nhau (Xnest) không sẵn sàng, hoặc rất có thể là GDM có " +"cấu hình sai." + +#: ../src/cut-n-paste/gdmcomm.c:497 ../src/gdmcomm.c:497 +msgid "Trying to change to an invalid virtual terminal number." +msgstr "Đang cố chuyển đổi sang một số thiết bị cuối ảo không hợp lệ." + +#: ../src/cut-n-paste/gdmcomm.c:501 ../src/gdmcomm.c:501 +msgid "" +"You do not seem to have authentication needed be for this operation. " +"Perhaps your .Xauthority file is not set up correctly." +msgstr "" +"Hình như bạn không có cách xác thực cần thiết cho thao tác này. Có lẽ tập " +"tin « .Xauthority » của bạn chưa được thiết lập cho đúng. " + +#: ../src/cut-n-paste/gdmcomm.c:505 ../src/gdmcomm.c:505 +msgid "Too many messages were sent to gdm and it hung upon us." +msgstr "" +"Quá nhiều thông điệp đã được gởi cho GDM nên nó ngắt kết nối đến chúng ta." + +#: ../src/cut-n-paste/gdmcomm.c:508 ../src/gdmcomm.c:508 +msgid "Unknown error occurred." +msgstr "Gặp lỗi không xác định." + +#: ../src/file-transfer-dialog.c:94 +#, c-format +msgid "Copying file: %u of %u" +msgstr "Đang sao chép tập tin: %u trên %u" + +#: ../src/file-transfer-dialog.c:122 +#: ../capplets/common/file-transfer-dialog.c:122 +#, c-format +msgid "Copying '%s'" +msgstr "Đang sao chép « %s »" + +#: ../src/file-transfer-dialog.c:193 +msgid "From URI" +msgstr "Từ URI" + +#: ../src/file-transfer-dialog.c:194 +msgid "URI currently transferring from" +msgstr "Hiện thời truyền từ URI này" + +#: ../src/file-transfer-dialog.c:201 +msgid "To URI" +msgstr "Tới URI" + +#: ../src/file-transfer-dialog.c:202 +msgid "URI currently transferring to" +msgstr "Hiện thời truyền đến URI này." + +#: ../src/file-transfer-dialog.c:209 +#: ../capplets/common/file-transfer-dialog.c:209 +msgid "Fraction completed" +msgstr "Phần hoàn tất" + +#: ../src/file-transfer-dialog.c:210 +#: ../capplets/common/file-transfer-dialog.c:210 +msgid "Fraction of transfer currently completed" +msgstr "Phần truyền hiện thời đã hoàn tất" + +#: ../src/file-transfer-dialog.c:217 +msgid "Current URI index" +msgstr "Chỉ mục URI hiện thời" + +#: ../src/file-transfer-dialog.c:218 +msgid "Current URI index - starts from 1" +msgstr "Chỉ mục URI hiện thời — bắt đầu từ 1" + +#: ../src/file-transfer-dialog.c:225 +msgid "Total URIs" +msgstr "URI tổng cộng" + +#: ../src/file-transfer-dialog.c:226 +msgid "Total number of URIs" +msgstr "Tổng số URI" + +#: ../capplets/common/file-transfer-dialog.c:448 +msgid "Connecting..." +msgstr "Đang kết nối..." + +#: ../src/gnome-screensaver-command.c:59 ../src/gnome-screensaver-command.c:58 +msgid "Causes the screensaver to exit gracefully" +msgstr "Làm cho bộ bảo vệ màn hình thoát cho đúng." + +#: ../src/gnome-screensaver-command.c:61 ../src/gnome-screensaver-command.c:60 +msgid "Query the state of the screensaver" +msgstr "Truy vấn tính trạng của bộ bảo vệ màn hình." + +#: ../src/gnome-screensaver-command.c:63 ../src/gnome-screensaver-command.c:62 +msgid "Tells the running screensaver process to lock the screen immediately" +msgstr "Báo tiến trình bảo vệ màn hình đang chạy phải khoá màn hình ngay." + +#: ../src/gnome-screensaver-command.c:65 ../src/gnome-screensaver-command.c:64 +msgid "If the screensaver is active then switch to another graphics demo" +msgstr "" +"Nếu bộ bảo vệ màn hình có hoạt động thì chuyển đổi sang một chứng minh đồ " +"họa khác." + +#: ../src/gnome-screensaver-command.c:67 ../src/gnome-screensaver-command.c:66 +msgid "Turn the screensaver on (blank the screen)" +msgstr "Khởi chạy bộ bảo vệ màn hình (làm trắng màn hình)" + +#: ../src/gnome-screensaver-command.c:69 ../src/gnome-screensaver-command.c:68 +msgid "If the screensaver is active then deactivate it (un-blank the screen)" +msgstr "Nếu bộ bảo vệ màn hình có hoạt động thì tắt nó (bỏ trắng màn hình)" + +#: ../src/gnome-screensaver-command.c:71 ../src/gnome-screensaver-command.c:70 +msgid "Disable running graphical themes while blanked" +msgstr "Bật các sắc thái đồ họa đang chạy trong khi bị trắng." + +#: ../src/gnome-screensaver-command.c:73 ../src/gnome-screensaver-command.c:72 +msgid "Enable running graphical themes while blanked (if applicable)" +msgstr "" +"Hiệu lực các sắc thái đồ họa đang chạy trong khi bị trắng (nếu thích hợp)" + +#: ../src/gnome-screensaver-command.c:75 ../src/gnome-screensaver-command.c:74 +msgid "Poke the running screensaver to simulate user activity" +msgstr "" +"Thức bộ bảo vệ màn hình đang chạy, để mô phỏng hoạt động của người dùng." + +#: ../src/gnome-screensaver-dialog.c:53 ../src/gnome-screensaver.c:52 +msgid "Version of this application" +msgstr "Phiên bản ứng dụng này" + +#: ../src/gnome-screensaver-command.c:202 +#: ../src/gnome-screensaver-command.c:201 +#, c-format +msgid "The screensaver is %s\n" +msgstr "Bộ bảo vệ màn hình là %s\n" + +#: ../src/gnome-screensaver-command.c:202 +#: ../src/gnome-screensaver-command.c:201 +msgid "active" +msgstr "hoạt động" + +#: ../src/gnome-screensaver-command.c:202 +#: ../src/gnome-screensaver-command.c:201 +msgid "inactive" +msgstr "không hoạt động" + +#: ../src/gnome-screensaver-dialog.c:104 ../src/gnome-screensaver-dialog.c:51 +msgid "Show debugging output" +msgstr "Hiện dữ liệu xuất gỡ lỗi" + +#: ../src/gnome-screensaver-dialog.c:108 ../src/gnome-screensaver-dialog.c:55 +msgid "Show the logout button" +msgstr "Hiện cái nút đăng xuất" + +#: ../src/gnome-screensaver-dialog.c:110 +msgid "Command to invoke from the logout button" +msgstr "Lệnh cần chạy từ cái nút đăng xuất" + +#: ../src/gnome-screensaver-dialog.c:112 +msgid "Show the switch user button" +msgstr "Hiện cái nút chuyển đổi người dùng" + +#: ../libgnomedb/gnome-db-matrix.c:2189 ../libgnomedb/gnome-db-matrix.c:2322 +msgid "Disabled" +msgstr "Đã tắt" + +#: ../src/gnome-screensaver-preferences.c:393 +msgid "Blank screen" +msgstr "Màn hình trắng" + +#: ../plug-ins/gimpressionist/size.c:149 +msgid "Random" +msgstr "Ngẫu nhiên" + +#: ../src/gnome-screensaver-preferences.c:692 +#: ../src/gnome-screensaver-preferences.c:616 +msgid "Invalid screensaver theme" +msgstr "Sắc thái bảo vệ màn hình không hợp lệ" + +#: ../src/gnome-screensaver-preferences.c:695 +#: ../src/gnome-screensaver-preferences.c:619 +msgid "This file does not appear to be a valid screensaver theme." +msgstr "Hình như tập tin này không phải là sắc thái bảo vệ màn hình hợp lệ." + +#: ../src/gnome-torrent.in:245 +#, c-format +msgid "%d hour" +msgid_plural "%d hour" +msgstr[0] "%d giờ" + +#: ../src/gnome-torrent.in:246 ../src/gnome-torrent.in:249 +#, c-format +msgid "%d minute" +msgid_plural "%d minute" +msgstr[0] "%d phút" + +#: ../src/gnome-torrent.in:250 ../src/gnome-torrent.in:253 +#, c-format +msgid "%d second" +msgid_plural "%d second" +msgstr[0] "%d giây" + +#: ../sources/rb-playlist-source-recorder.c:419 +#, c-format +msgid "%s %s %s" +msgstr "%s %s %s" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. hour:minutes +#. minutes:seconds +#. #-#-#-#-# libmimedir.vi.po (libmimedir HEADnReport-Msgid-Bugs-To: ) #-#-#-#-# +#. Translators: pcode city +#: ../src/gnome-screensaver-preferences.c:835 +#: ../mimedir/mimedir-vcard-address.c:864 +#: ../mimedir/mimedir-vcard-address.c:873 +#, c-format +msgid "%s %s" +msgstr "%s %s" + +# #-#-#-#-# straw.po (straw) #-#-#-#-# +# Variable: don't translate / Biến: đừng dịch +#: ../sources/rb-playlist-source-recorder.c:425 ../src/lib/Application.py:155 +#: ../src/lib/subscribe.py:414 ../src/lib/subscribe.py:415 +#, c-format, python-format +msgid "%s" +msgstr "%s" + +#: ../sources/rb-playlist-source-recorder.c:428 +msgid "0 seconds" +msgstr "0 giây" + +#: ../src/gnome-screensaver-preferences.c:896 ../src/properties.c:255 +#: ../src/gnome-screensaver-preferences.c:819 +msgid "Could not load the main interface" +msgstr "Không thể tải giao diện chính" + +#: ../src/gnome-screensaver-preferences.c:898 +#: ../src/gnome-screensaver-preferences.c:821 +msgid "Please make sure that the screensaver is properly installed" +msgstr "Hãy chắc là bộ bảo vệ màn hình được cài đặt đúng." + +#: ../src/gnome-screensaver.c:57 +msgid "Don't become a daemon" +msgstr "Đừng chạy trong nền" + +#: ../src/gnome-screensaver.c:58 ../shell/main.c:149 +msgid "Enable debugging code" +msgstr "Bật chạy mã gỡ lỗi" + +#: ../src/gs-listener-dbus.c:1014 ../src/gs-listener-dbus.c:852 +msgid "failed to register with the message bus" +msgstr "việc đăng ký với mạch nối thông điệp bị lỗi" + +#: ../src/gs-listener-dbus.c:1024 +msgid "not connected to the message bus" +msgstr "chưa kết nối đến mạch nối thông điệp" + +#: ../src/gs-listener-dbus.c:1033 ../src/gs-listener-dbus.c:861 +msgid "screensaver already running in this session" +msgstr "bộ bảo vệ màn hình đang chạy trong phiên chạy này" + +#: ../src/gs-lock-plug.c:346 ../src/gs-lock-plug.c:248 +msgid "Checking password..." +msgstr "Đang kiểm tra mật khẩu..." + +#: ../src/gs-lock-plug.c:389 +msgid "Time has expired." +msgstr "Quá giờ." + +#: ../src/gs-lock-plug.c:414 ../src/gs-lock-plug.c:330 +msgid "You have the Caps Lock key on." +msgstr "Bạn đã bấm phím Khoá Chữ Hoa (CapsLock)" + +#: ../src/gs-lock-plug.c:691 +msgid "That password was incorrect." +msgstr "Bạn mới gỏ một mật khẩu không đúng." + +#: ../src/gs-lock-plug.c:717 ../src/gs-lock-plug.c:750 +#: ../src/gs-lock-plug.c:561 ../src/gs-lock-plug.c:602 +msgid "_Unlock" +msgstr "_Bỏ khoá" + +#: ../src/gs-lock-plug.c:720 ../src/gs-lock-plug.c:747 +msgid "_Switch User..." +msgstr "_Chuyển đổi người dùng..." + +#: ../src/gs-lock-plug.c:1498 +msgid "S_witch to user:" +msgstr "C_huyển đổi sang người dùng:" + +#: ../src/gs-lock-plug.c:1545 ../src/gs-lock-plug.c:1372 +msgid "Log _Out" +msgstr "Đăng _xuất" + +#: ../data/pessulus.glade.h:1 ../admin-tool/lockdown/pessulus.glade.h:1 +msgid "Disabled Applets" +msgstr "Tiểu dụng bị tắt" + +#: ../data/pessulus.glade.h:2 ../admin-tool/lockdown/pessulus.glade.h:2 +msgid "Safe Protocols" +msgstr "Giao thức an toàn" + +#: ../data/pessulus.glade.h:3 ../admin-tool/lockdown/pessulus.glade.h:3 +msgid "Disable _unsafe protocols" +msgstr "Tắt các giao thức _bất an" + +#: ../data/pessulus.glade.h:4 ../admin-tool/lockdown/pessulus.glade.h:4 +#: ../data/epiphany.desktop.in.h:2 ../src/window-commands.c:779 +msgid "Epiphany Web Browser" +msgstr "Trình Duyệt Mạng Epiphany" + +#: ../data/pessulus.glade.h:6 ../admin-tool/lockdown/pessulus.glade.h:6 +msgid "Lockdown Editor" +msgstr "Bộ hiệu chỉnh khoá xuống" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. "A control area." +#: ../Sensors/StarterBar/__init__.py:627 ../src/orca/rolenames.py:338 +msgid "Panel" +msgstr "Bảng điều khiển" + +#: ../Pessulus/lockdownbutton.py:99 +#: ../admin-tool/lockdown/lockdownbutton.py:99 +msgid "Click to make this setting not mandatory" +msgstr "Nhắp chuột để làm cho thiết lập này không bắt buộc" + +#: ../Pessulus/lockdownbutton.py:101 +#: ../admin-tool/lockdown/lockdownbutton.py:101 +msgid "Click to make this setting mandatory" +msgstr "Nhắp chuột để làm cho thiết lập này bắt buộc" + +#: ../Pessulus/maindialog.py:47 ../admin-tool/lockdown/maindialog.py:47 +msgid "Disable _command line" +msgstr "Tắt dòng _lệnh" + +#: ../Pessulus/maindialog.py:48 ../admin-tool/lockdown/maindialog.py:48 +msgid "Disable _printing" +msgstr "Tắt khả năng _in" + +#: ../Pessulus/maindialog.py:49 ../admin-tool/lockdown/maindialog.py:49 +msgid "Disable print _setup" +msgstr "Tắt _thiết lập in" + +#: ../Pessulus/maindialog.py:50 ../admin-tool/lockdown/maindialog.py:50 +msgid "Disable save to _disk" +msgstr "Tắt khả năng lưu vào _đĩa" + +#: ../Pessulus/maindialog.py:52 ../admin-tool/lockdown/maindialog.py:52 +msgid "_Lock down the panels" +msgstr "_Khoá xuống các Bảng điều khiển" + +#: ../Pessulus/maindialog.py:53 ../admin-tool/lockdown/maindialog.py:53 +msgid "Disable force _quit" +msgstr "Tắt khả năng buộc t_hoát" + +#: ../Pessulus/maindialog.py:54 ../admin-tool/lockdown/maindialog.py:54 +msgid "Disable lock _screen" +msgstr "Tắt khả năng khoá _màn hình" + +#: ../Pessulus/maindialog.py:55 ../admin-tool/lockdown/maindialog.py:55 +msgid "Disable log _out" +msgstr "Tắt khả năng đăng _xuất" + +#: ../Pessulus/maindialog.py:57 ../admin-tool/lockdown/maindialog.py:57 +msgid "Disable _quit" +msgstr "Tắt khả năng t_hoát" + +#: ../Pessulus/maindialog.py:58 ../admin-tool/lockdown/maindialog.py:58 +msgid "Disable _arbitrary URL" +msgstr "Tắt địa chỉ Mạng tù_y ý" + +#: ../Pessulus/maindialog.py:59 ../admin-tool/lockdown/maindialog.py:59 +msgid "Disable _bookmark editing" +msgstr "Tắt hiệu chỉnh đánh _dấu" + +#: ../Pessulus/maindialog.py:60 ../admin-tool/lockdown/maindialog.py:60 +msgid "Disable _history" +msgstr "Tắt _lược sử" + +#: ../Pessulus/maindialog.py:61 ../admin-tool/lockdown/maindialog.py:61 +msgid "Disable _javascript chrome" +msgstr "Tắt crom _JavaScript" + +#: ../Pessulus/maindialog.py:62 ../admin-tool/lockdown/maindialog.py:62 +msgid "Disable _toolbar editing" +msgstr "Tắt hiệu chỉnh thanh _công cụ" + +#: ../Pessulus/maindialog.py:63 ../admin-tool/lockdown/maindialog.py:63 +#: ../src/f-spot.glade.h:166 +msgid "_Fullscreen" +msgstr "Toàn _màn hình" + +#: ../Pessulus/maindialog.py:64 ../admin-tool/lockdown/maindialog.py:64 +msgid "Hide _menubar" +msgstr "Ẩn thanh trình đơ_n" + +#: ../nact/nact.desktop.in.h:1 +msgid "Add items to the Nautilus popup menu" +msgstr "Thêm mục vào trình đơn bật lên Nautilus" + +#: ../nact/nact.desktop.in.h:2 +msgid "Nautilus Actions Configuration" +msgstr "Cấu hình Hành động Nautilus" + +#: ../src/f-spot.glade.h:3 ../data/glade/jamboree.glade.h:1 +#: dselect/pkgdisplay.cc:48 info/session.c:3674 info/session.c:3679 +#: ../objects/UML/class.c:189 ../objects/UML/class.c:191 +#: ../objects/UML/class.c:193 ../objects/UML/class.c:195 +#: ../objects/UML/class.c:197 ../objects/UML/class.c:199 +#: ../ui/welcome.glade.h:1 ../gnome/applet/wireless-applet.glade.h:1 +#: ../glade/straw.glade.h:1 +msgid " " +msgstr " " + +#: ../nact/nautilus-actions-config.glade.h:2 +#: ../data/glade/task-dialog.glade.h:1 ../glade/straw.glade.h:2 +msgid " " +msgstr " " + +#: ../nact/nautilus-actions-config.glade.h:3 +msgid "(C) 2005 Frederic Ruaudel " +msgstr "Bản quyền © năm 2005 Frederic Ruaudel " + +#: ../nact/nautilus-actions-config.glade.h:5 +#, no-c-format +msgid "%% : a percent sign" +msgstr "%%: dấu phần trăm" + +#: ../nact/nautilus-actions-config.glade.h:7 +#, no-c-format +msgid "" +"%M : space-separated list of the selected file(s)/folder(s) with " +"their full paths" +msgstr "" +"%M : danh sách những tập tin/thư mục đã chọn với toàn đường dẫn, định " +"giới bằng dấu cách." + +#: ../nact/nautilus-actions-config.glade.h:9 +#, no-c-format +msgid "%U : username of the gnome-vfs URI" +msgstr "%U : tên người dùng của URI gnome-vfs" + +#: ../nact/nautilus-actions-config.glade.h:11 +#, no-c-format +msgid "%d : base folder of the selected file(s)" +msgstr "%d : thư mục cơ bản của tập tin đã chọn" + +#: ../nact/nautilus-actions-config.glade.h:13 +#, no-c-format +msgid "" +"%f : the name of the selected file or the 1st one if many are selected" +msgstr "" +"%f : tên tập tin đã chọn hay tên tập tin thứ nhất nếu đã chọn nhiều" + +#: ../nact/nautilus-actions-config.glade.h:15 +#, no-c-format +msgid "%h : hostname of the gnome-vfs URI" +msgstr "%h : tên máy của URI gnome-vfs" + +#: ../nact/nautilus-actions-config.glade.h:17 +#, no-c-format +msgid "" +"%m : space-separated list of the basenames of the selected\n" +"file(s)/folder(s)" +msgstr "" +"%m : danh sách tên cơ bản của những tập tin/thư mục đã chọn, định " +"giới bằng dấu cách." + +#: ../nact/nautilus-actions-config.glade.h:20 +#, no-c-format +msgid "%s : scheme of the gnome-vfs URI" +msgstr "%s : lược đồ của URI gnome-vfs" + +#: ../nact/nautilus-actions-config.glade.h:22 +#, no-c-format +msgid "%u : gnome-vfs URI" +msgstr "%u : URI gnome-vfs" + +#: ../nact/nautilus-actions-config.glade.h:23 +msgid "Action" +msgstr "Hành động" + +#: ../nact/nautilus-actions-config.glade.h:24 +msgid "Appears if scheme is in this list" +msgstr "Xuất hiện nếu lược đồ có trong danh sách này" + +#: ../nact/nautilus-actions-config.glade.h:25 +msgid "Appears if selection contains" +msgstr "Xuất hiện nếu vùng chọn chứa" + +#: ../nact/nautilus-actions-config.glade.h:26 +msgid "File Pattern" +msgstr "Mẫu tập tin" + +#: ../nact/nautilus-actions-config.glade.h:27 +msgid "Nautilus Menu Item" +msgstr "Mục trình đơn Nautilus" + +#: ../nact/nautilus-actions-config.glade.h:29 +#, no-c-format +msgid "e.g., %s" +msgstr "v.d., %s" + +#: ../nact/nautilus-actions-config.glade.h:30 +msgid "Parameter Legend" +msgstr "Chú giải tham số" + +#: ../nact/nautilus-actions-config.glade.h:31 +msgid "" +"A string with joker ? or * that will be used to match the files. You can " +"match several file patterns by separating them with a semi-colon ;" +msgstr "" +"Chuỗi có ký tự đặc biệt « ? » hay « * » sẽ được dùng để khớp những tập tin. " +"Bạn có thể khớp nhiều mẫu định giới bằng dấu chấm phẩy « ; »." + +#: ../nact/nautilus-actions-config.glade.h:32 +msgid "Appears if selection has multiple files or folders" +msgstr "Xuất hiện nếu vùng chọn có nhiều tập tin hay thư mục" + +#: ../plug-ins/common/postscript.c:3154 ../plug-ins/fits/fits.c:1007 +#: ../widgets/gtk+.xml.in.h:14 src/settings.c:721 +#: libexif/olympus/mnote-olympus-entry.c:466 +msgid "Automatic" +msgstr "Tự động" + +#: ../nact/nautilus-actions-config.glade.h:35 +msgid "" +"Check this box if you want to get back all your configurations from the " +"version of Nautilus-actions 0.7.1 or lesser." +msgstr "" +"Hãy đánh dấu trong hộp này nếu bạn muốn nhận lại các cấu hình từ phiên bản " +"Nautilus-actions 0.7.1 hay trước." + +#: ../nact/nautilus-actions-config.glade.h:36 +msgid "Click to add a new scheme" +msgstr "Nhấn để thêm lược đồ mới" + +#: ../nact/nautilus-actions-config.glade.h:37 +msgid "Click to choose a command from the file chooser dialog" +msgstr "Nhấn để chọn lệnh từ hộp thoại bộ chọn tập tin" + +#: ../nact/nautilus-actions-config.glade.h:38 +msgid "" +"Click to choose a custom icon from a file instead of a predefined icon from " +"the drop-down list" +msgstr "" +"Nhấn để chọn biểu tượng riêng từ tập tin thay vào biểu tượng định sẵn từ " +"danh sách thả xuống" + +#: ../nact/nautilus-actions-config.glade.h:39 +msgid "Click to remove the selected scheme" +msgstr "Nhấn để gỡ bỏ lược đồ đã chọn" + +#: ../nact/nautilus-actions-config.glade.h:40 +msgid "" +"Click to see the list of special tokens you can use in the parameter field" +msgstr "" +"Nhấn để xem danh sách các hiệu bài đặc biệt có thể gõ vào trường tham số" + +#: ../nact/nautilus-actions-config.glade.h:41 +#: ../gtksourceview/language-specs/sql.lang.h:9 +msgid "Conditions" +msgstr "Điều kiện" + +#: ../nact/nautilus-actions-config.glade.h:42 +msgid "Export existing configs" +msgstr "Xuất cấu hình đã có" + +#: ../nact/nautilus-actions-config.glade.h:43 +msgid "File to Import" +msgstr "Tập tin cần nhập" + +#: ../nact/nautilus-actions-config.glade.h:44 +msgid "GConf schema description file (Nautilus-actions v1.x and later)" +msgstr "Tập tin diễn tả giản đồ GConf (Nautilus-actions phiên bản 1.x và sau)" + +#: ../glade/glade_menu_editor.c:813 ../src/f-spot.glade.h:89 +#: ../glade/gbwidgets/gbbutton.c:122 ../glade/gbwidgets/gbcheckbutton.c:89 +#: ../glade/gbwidgets/gbimage.c:107 ../glade/gbwidgets/gbmenutoolbutton.c:89 +#: ../glade/gbwidgets/gbradiobutton.c:130 +#: ../glade/gbwidgets/gbradiotoolbutton.c:137 +#: ../glade/gbwidgets/gbtogglebutton.c:92 +#: ../glade/gbwidgets/gbtoggletoolbutton.c:94 +#: ../glade/gbwidgets/gbtoolbutton.c:108 ../glade/gbwidgets/gbwindow.c:297 +#: ../glade/glade_menu_editor.c:814 +msgid "Icon:" +msgstr "Biểu tượng:" + +#: ../nact/nautilus-actions-config.glade.h:46 +msgid "Import all my old configs" +msgstr "Nhập mọi cấu hình cũ của tôi" + +#: ../nact/nautilus-actions-config.glade.h:47 +msgid "Import new configurations" +msgstr "Nhập cấu hình mới" + +#: ../nact/nautilus-actions-config.glade.h:48 +msgid "Import/Export" +msgstr "Nhập/Xuất" + +#: ../nact/nautilus-actions-config.glade.h:49 +msgid "Import/Export Settings" +msgstr "Thiết lập Nhập/Xuất" + +#: ../nact/nautilus-actions-config.glade.h:50 +msgid "Label of the menu item in the Nautilus popup menu" +msgstr "Nhãn của mục trình đơn trong trình đơn bật lên Nautilus" + +#: ../glade/gnome/gnomehref.c:68 +msgid "Label:" +msgstr "Nhãn:" + +#: ../nact/nautilus-actions-config.glade.h:52 +msgid "Menu Item & Action" +msgstr "Mục trình đơn và hành động" + +#: ../nact/nautilus-actions-config.glade.h:53 +msgid "" +"Nautilus Action Configuration Tool\n" +"Application to configure Nautilus Action extension" +msgstr "" +"Công cụ Cấu hình Hành động Nautilus\n" +"Ứng dụng cần cấu hình phần mở rộng Hành động Nautilus" + +#: ../nact/nautilus-actions-config.glade.h:55 +msgid "Nautilus Action Editor" +msgstr "Bộ sửa đổi hành động Nautilus" + +#: ../nact/nautilus-actions-config.glade.h:56 +msgid "Nautilus Actions" +msgstr "Hành động Nautilus" + +#: ../nact/nautilus-actions-config.glade.h:57 +msgid "Old XML config file (Nautilus-actions v0.x)" +msgstr "Tập tin cấu hình XML cũ (Nautilus-actions phiên bản 0.x)" + +#: ../nact/nautilus-actions-config.glade.h:58 +msgid "Only files" +msgstr "Chỉ tập tin" + +#: ../nact/nautilus-actions-config.glade.h:59 +msgid "Only folders" +msgstr "Chỉ thư mục" + +#: ../nact/nautilus-actions-config.glade.h:60 +msgid "" +"Parameters that will be sent to the program. Click on the 'Legend' button to " +"see the different replacement tokens" +msgstr "" +"Tham số sẽ được gởi cho chương trình. Hãy nhấn vào cái nút « Chú giải » để " +"xem những hiệu bài thay thế khác nhau." + +#: ../nact/nautilus-actions-config.glade.h:61 +#: ../objects/UML/class_dialog.c:1986 ../objects/UML/class_dialog.c:2083 +msgid "Parameters:" +msgstr "Tham số :" + +#: ../src/Dialog_Partition_Info.cc:168 ../src/Win_GParted.cc:232 +msgid "Path:" +msgstr "Đường dẫn:" + +#: ../nact/nautilus-actions-config.glade.h:63 +msgid "Project Website" +msgstr "Nơi Mạng của Dự án" + +#: ../nact/nautilus-actions-config.glade.h:64 +msgid "Save in Folder" +msgstr "Lưu vào thư mục" + +#: ../nact/nautilus-actions-config.glade.h:65 +msgid "Select the configurations you want to export :" +msgstr "Chọn những cấu hình cần xuất:" + +#: ../nact/nautilus-actions-config.glade.h:66 +msgid "" +"Select the configurations you want to export. Use Shift or Ctrl key to " +"select multiple one." +msgstr "" +"Chọn những cấu hình cần xuất. Hãy sử dụng phím Shift hay Ctrl để chọn nhiều " +"điều." + +#: ../nact/nautilus-actions-config.glade.h:67 +msgid "Select the file you want to import." +msgstr "Chọn tập tin cần nhập." + +#: ../nact/nautilus-actions-config.glade.h:68 +msgid "" +"Select the folder you want your config to be saved in. This folder must " +"exists." +msgstr "Hãy chọn thư mục nơi bạn muốn lưu cấu hình. Thư mục này phải tồn tại." + +#: ../nact/nautilus-actions-config.glade.h:69 +msgid "" +"Select the kind of files where you want your action to appear. If you don't " +"know what to choose, try selecting just 'file' which is the most common " +"choice. You can add a new scheme by clicking on the '+' button" +msgstr "" +"Hãy chọn kiểu tập tin nơi bạn muốn hành động xuất hiện. Nếu bạn chưa biết " +"nên chọn gì, thử chọn chỉ « tập tin » mà là sự chọn thường nhất. Có thể thêm " +"lược đồ mới bằng cách nhấn vào cái nút « + »." + +#: ../nact/nautilus-actions-config.glade.h:70 +msgid "" +"The command that will be launched by selecting the action in Nautilus popup " +"menu" +msgstr "" +"Lệnh sẽ được khởi chạy khi chọn hành động trong trình đơn bật lên Nautilus" + +#: ../nact/nautilus-actions-config.glade.h:71 +msgid "This software is licensed under the GNU Genaral Public License (GPL)" +msgstr "" +"Phần mềm này được phát hành với điều kiện của Quyền Công Chung Gnu (GPL)." + +#: ../nact/nautilus-actions-config.glade.h:72 +msgid "Tooltip of the menu item that will appear in the Nautilus statusbar" +msgstr "" +"Mẹo công cụ của mục trình đơn sẽ xuất hiện trong thanh trạng thái Nautilus" + +#: ../nact/nautilus-actions-config.glade.h:73 ../glade/glade_menu_editor.c:936 +#: ../glade/property.c:824 ../glade/glade_menu_editor.c:937 +msgid "Tooltip:" +msgstr "Mẹo công cụ :" + +#: ../nact/nautilus-actions-config.glade.h:74 +msgid "Type of configuration :" +msgstr "Kiểu cấu hinh:" + +#: ../nact/nautilus-actions-config.glade.h:75 ../eog.glade.h:28 +msgid "_Browse" +msgstr "_Duyệt" + +#: ../src/mainWindow.py:342 ../extensions/page-info/page-info-dialog.c:933 +#: main.c:1577 main.c:1678 ../glade/glade_menu_editor.c:418 +#: ../src/orca/rolenames.py:273 +msgid "Icon" +msgstr "Biểu tượng" + +#: ../gtk/gtklabel.c:322 ../gtk/gtktoolbutton.c:187 ../src/Database.cs:845 +#: ../glade/gbwidgets/gblabel.c:872 ../glade/glade_menu_editor.c:411 +#: ../src/glade-gtk.c:3518 ../widgets/gtk+.xml.in.h:110 +#: ../src/form-editor/widget-util.cc:191 ../src/menu-win.cc:256 +#: ../src/orca/rolenames.py:288 +msgid "Label" +msgstr "Nhãn" + +#: ../nact/nact.c:231 ../nact/nact-editor.c:672 +#: ../nact/nact-import-export.c:346 +msgid "Could not load interface for Nautilus Actions Config Tool" +msgstr "Không thể tải giao diện cho Công cụ Cấu hình Hành động Nautilus" + +#. i18n notes: example strings for the command preview +#: ../nact/nact-utils.c:157 +msgid "/path/to" +msgstr "/đường_dẫn/đến" + +#: ../nact/nact-utils.c:158 ../nact/nact-utils.c:160 +msgid "file1.txt" +msgstr "tập_tin1.txt" + +#: ../nact/nact-utils.c:158 +msgid "file2.txt" +msgstr "tập_tin2.txt" + +#: ../nact/nact-utils.c:159 ../nact/nact-utils.c:160 +msgid "folder1" +msgstr "thư_mục1" + +#: ../nact/nact-utils.c:159 +msgid "folder2" +msgstr "thư_mục2" + +#: ../nact/nact-utils.c:162 +msgid "test.example.net" +msgstr "thử.ví_dụ.net" + +#: ../nact/nact-utils.c:163 +msgid "file.txt" +msgstr "tập_tin.txt" + +#: ../nact/nact-utils.c:164 gphoto2/main.c:1668 +#: ../freedesktop.org.xml.in.h:334 +msgid "folder" +msgstr "thư mục" + +#. i18n notes : scheme name set for a new entry in the scheme list +#: ../nact/nact-editor.c:435 +msgid "new-scheme" +msgstr "lược-đồ-mới" + +#: ../nact/nact-editor.c:436 +msgid "New Scheme Description" +msgstr "Mô tả Lược đồ Mới" + +#: ../nact/nact-editor.c:522 +msgid "Scheme" +msgstr "Lược đồ" + +#: ../nact/nact-editor.c:688 +msgid "Icon of the menu item in the Nautilus popup menu" +msgstr "Biểu tượng của mục trình đơn trong trình đơn bật lên Nautilus" + +#: ../nact/nact-editor.c:716 +msgid "Add a New Action" +msgstr "Thêm hành động mới" + +#: ../nact/nact-editor.c:720 +#, c-format +msgid "Edit Action \"%s\"" +msgstr "Sửa đổi hành động « %s »" + +#: ../nact/nact-prefs.c:164 +#, c-format +msgid "%sLocal Files" +msgstr "%sTập tin cục bộ" + +#. i18n notes : description of 'sftp' scheme +#: ../nact/nact-prefs.c:166 +#, c-format +msgid "%sSSH Files" +msgstr "%sTập tin SSH" + +#. i18n notes : description of 'smb' scheme +#: ../nact/nact-prefs.c:168 +#, c-format +msgid "%sWindows Files" +msgstr "%sTập tin Windows" + +#. i18n notes : description of 'ftp' scheme +#: ../nact/nact-prefs.c:170 +#, c-format +msgid "%sFTP Files" +msgstr "%sTập tin FTP" + +#. i18n notes : description of 'dav' scheme +#: ../nact/nact-prefs.c:172 +#, c-format +msgid "%sWebdav Files" +msgstr "%sTập tin Webdav" + +#. vim:ts=3:sw=3:tw=1024:cin +#: ../config/config_newaction.schemas.in.in.h:1 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:86 +msgid "'true' if the selection can have several items, 'false' otherwise" +msgstr "" +"« true » (đúng) nếu vùng chọn có thể chứa vài mục; không thì « false " +"» (không đúng)" + +#: ../config/config_newaction.schemas.in.in.h:2 +msgid "'true' if the selection must have files, 'false' otherwise" +msgstr "" +"« true » (đúng) nếu vùng chọn phải chứa tập tin; không thì « false » (không " +"đúng)" + +#: ../config/config_newaction.schemas.in.in.h:3 +msgid "'true' if the selection must have folders, 'false' otherwise" +msgstr "" +"« true » (đúng) nếu vùng chọn phải chứa thư mục; không thì « false » (không " +"đúng)" + +#: ../config/config_newaction.schemas.in.in.h:4 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:79 +msgid "" +"A list of strings with joker '*' or '?' to match the selected file(s)/folder" +"(s). Each selected items must match at least one of the patterns for the " +"action to appear" +msgstr "" +"Danh sách chuỗi chứa ký tự đặc biệt « ? » hay « * » để khớp tập tin/thư mục " +"đã chọn. Mỗi mục đã chọn phải khớp với ít nhất một mẫu, để gây ra hành động " +"xuất hiện." + +#: ../config/config_newaction.schemas.in.in.h:5 +msgid "" +"Defines the list of valid GnomeVFS schemes to be matched with the selected " +"items. The GnomeVFS scheme is the protocol used to access the files. The " +"keyword to use is the one used in the GnomeVFS URI. Example of GnomeVFS " +"URI : file:///tmp/foo.txt sftp:///root@test.example.net/tmp/foo.txt The most " +"common schemes are : 'file' : local files 'sftp' : files accessed via SSH " +"'ftp' : files accessed via FTP 'smb' : files accessed via Samba (Windows " +"share) 'dav' : files accessed via WebDav All GnomeVFS schemes used by " +"Nautilus can be used here." +msgstr "" +"Định nghĩa danh sách lược đồ GnomeVFS hợp lệ để khớp những mục đã chọn. Lược " +"đồ GnomeVFS là giao thức được dùng để truy cập những tập tin. Từ khoá cần " +"dùng là điều dùng trong URI GnomeVFS (v.d. ). Những lược đồ thường nhất:\n" +" • file \t— tập tin cục bộ\n" +" • sftp \t— tập tin được truy cập bằng SSH\n" +" • ftp \t— tập tin được truy cập bằng FTP\n" +" • smb\t— tập tin được truy cập bằng Samba (chia sẻ Windows)\n" +" • dav\t— tập tin được truy cập bằng WebDav.\n" +"Ở đây có thể sử dụng lược đồ nào do Nautilus dùng." + +#: ../config/config_newaction.schemas.in.in.h:6 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:87 +msgid "" +"If you need one or more files or folders to be selected, set this key to " +"'true'. If you want just one file or folder, set 'false'" +msgstr "" +"Nếu bạn cần chọn một hay nhiều tập tin hay thư mục, hãy đặt khoá này là « " +"true » (đúng). Nếu bạn muốn chỉ một tập tin hay thư mục, hãy đặt « false " +"» (không đúng)." + +#: ../config/config_newaction.schemas.in.in.h:7 +msgid "Manage Actions" +msgstr "Quản lý hành động" + +#: ../config/config_newaction.schemas.in.in.h:8 +msgid "Manage your actions using NACT, the configuration tool" +msgstr "Quản lý các hành động bằng công cụ cấu hình NACT" + +#: ../config/config_newaction.schemas.in.in.h:9 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:71 +msgid "The icon of the menu item" +msgstr "Biểu tượng của mục trình đơn" + +#: ../config/config_newaction.schemas.in.in.h:10 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:72 +msgid "" +"The icon of the menu item that will appear next to the label in the Nautilus " +"popup menu when the selection matches the appearance conditions settings" +msgstr "" +"Biểu tượng của mục trình đơn, sẽ xuất hiện ở cạnh nhãn trong trình đơn bật " +"lên Nautilus khi vùng chọn khớp với thiết lập điều kiện xuất hiện" + +#. GConf description strings : +#: ../config/config_newaction.schemas.in.in.h:11 +#: ../utils/nautilus-actions-new-config.c:48 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:67 +msgid "The label of the menu item" +msgstr "Nhãn của mục trình đơn" + +#: ../config/config_newaction.schemas.in.in.h:12 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:68 +msgid "" +"The label of the menu item that will appear in the Nautilus popup menu when " +"the selection matches the appearance condition settings" +msgstr "" +"Nhãn của mục trình đơn, sẽ xuất hiện ở cạnh nhãn trong trình đơn bật lên " +"Nautilus khi vùng chọn khớp với thiết lập điều kiện xuất hiện" + +#: ../config/config_newaction.schemas.in.in.h:13 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:88 +msgid "The list of GnomeVFS schemes where the selected files should be located" +msgstr "Danh sách các lược đồ GnomeVFS nơi cần định vị những tập tin đã chọn" + +#: ../config/config_newaction.schemas.in.in.h:14 +msgid "The list of patterns to match the selected file(s)/folder(s)" +msgstr "Danh sách các mẫu cần khớp với những tập tin/thư mục đã chọn" + +#: ../config/config_newaction.schemas.in.in.h:15 +#: ../utils/nautilus-actions-new-config.c:52 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:75 +msgid "The parameters of the command" +msgstr "Tham số của lệnh" + +#: ../config/config_newaction.schemas.in.in.h:17 +#, no-c-format +msgid "" +"The parameters of the command to start when the user selects the menu item " +"in the Nautilus popup menu. The parameters can contain some special tokens " +"which are replaced by Nautilus informations before starting the command : %" +"d : base folder of the selected file(s) %f : the name of the selected file " +"or the 1st one if many are selected %m : space-separated list of the " +"basenames of the selected file(s)/folder(s) %M : space-separated list of the " +"selected file(s)/folder(s), with their full paths %u : gnome-vfs URI %s : " +"scheme of the gnome-vfs URI %h : hostname of the gnome-vfs URI %U : username " +"of the gnome-vfs URI %% : a percent sign" +msgstr "" +"Những tham số của lệnh cần chạy khi người dùng chọn mục trình đơn trong " +"trình đơn bật lên Nautilus. Những tham số có thể chứa một số hiệu bài đặc " +"biệt, mà được thay thế bằng thông tin Nautilus trước khi khởi chạy lệnh:\n" +" • %d\t— thư mục cơ bản của tập tin đã chọn\n" +" • %f\t\t— tên của tập tin đã chọn, hay điều thứ nhất nếu có nhiều tập tin\n" +" • %m\t— danh sách tên cơ bản của các tập tin/thư mục, định giới bằng dấu " +"cách\n" +" • %M\t— danh sách những tập tin/thư mục đã chọn với toàn đường dẫn định " +"giới bằng dấu cách\n" +" • %u\t— URI Gnome-VFS\n" +" • %s\t— lược đồ của URI Gnome-VFS\n" +" • %h\t— tên máy của URI Gnome-VFS\n" +" • %U\t— tên người dùng của URI Gnome-VFS\n" +" • %%\t— dấu phần trăm" + +#: ../config/config_newaction.schemas.in.in.h:18 +#: ../utils/nautilus-actions-new-config.c:51 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:73 +msgid "The path of the command" +msgstr "Đường dẫn của lệnh" + +#: ../config/config_newaction.schemas.in.in.h:19 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:74 +msgid "" +"The path of the command to start when the user select the menu item in the " +"Nautilus popup menu" +msgstr "" +"Đường dẫn của lệnh cần chạy khi người dùng chọn mục trình đơn trong trình " +"đơn bật lện Nautilus" + +#: ../config/config_newaction.schemas.in.in.h:20 +#: ../utils/nautilus-actions-new-config.c:49 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:69 +msgid "The tooltip of the menu item" +msgstr "Mẹo công cụ của mục trình đơn" + +#: ../config/config_newaction.schemas.in.in.h:21 +msgid "" +"The tooltip of the menu item that will appear in the Nautilus statusbar when " +"the user points the mouse to the Nautilus popup menu item." +msgstr "" +"Mẹo công cụ của mục trình đơn sẽ xuất hiện trong thanh trạng thái Nautilus " +"khi người dùng chỉ con chuột đến mục trình đơn bật lệnh Nautilus" + +#: ../config/config_newaction.schemas.in.in.h:22 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:91 +msgid "The version of the configuration format" +msgstr "Phiên bản của khuôn dạng cấu hình" + +#: ../config/config_newaction.schemas.in.in.h:23 +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:92 +msgid "" +"The version of the configuration format that will be used to manage backward " +"compatibility" +msgstr "" +"Phiên bản của khuôn dạng cấu hình sẽ được dùng để quản lý cách tương thích " +"ngược" + +#: ../config/config_newaction.schemas.in.in.h:24 +msgid "" +"This setting is tied with the 'isdir' setting. Here are the valid " +"combinations : - 'isfile' is 'true' and 'isdir' is 'false' : the selection " +"must holds only files - 'isfile' is 'false' and 'isdir' is 'true' : the " +"selection must holds only folders - 'isfile' is 'true' and 'isdir' is " +"'true' : the selection can holds both files and folders - 'isfile' is " +"'false' and 'isdir' is 'false' : invalid combination" +msgstr "" +"Thiết lập này được liên quan đến thiết lập « isdir » (là thư mục). Những tổ " +"hợp đúng:\n" +" • « isfile » (là tập tin) là « true » (đúng) còn « isdir » (là thư mục) là " +"« false » (không đúng): vùng chọn phải chứa chỉ tập tin\n" +" • « isfile » (là tập tin) là « false » (không đúng) còn « isdir » (là thư " +"mục) là « true » (đúng): vùng chọn phải chứa chỉ thư mục\n" +" • « isfile » (là tập tin) là « true » (đúng) và « isdir » (là thư mục) cũng " +"là « true » (đúng): vùng chọn có thể chứa cả tập tin lẫn thư mục đều\n" +" • « isfile » (là tập tin) là « false » (không đúng) và « isdir » (là thư " +"mục) cũng là « false » (không đúng): tổ hợp không hợp lệ." + +#: ../config/config_newaction.schemas.in.in.h:25 +msgid "" +"This setting is tied with the 'isfile' setting. Here are the valid " +"combinations : - 'isfile' is 'true' and 'isdir' is 'false' : the selection " +"must holds only files - 'isfile' is 'false' and 'isdir' is 'true' : the " +"selection must holds only folders - 'isfile' is 'true' and 'isdir' is " +"'true' : the selection can holds both files and folders - 'isfile' is " +"'false' and 'isdir' is 'false' : invalid combination" +msgstr "" +"Thiết lập này được liên quan đến thiết lập « isfile » (là tập tin). Những tổ " +"hợp đúng:\n" +" • « isfile » (là tập tin) là « true » (đúng) còn « isdir » (là thư mục) là " +"« false » (không đúng): vùng chọn phải chứa chỉ tập tin\n" +" • « isfile » (là tập tin) là « false » (không đúng) còn « isdir » (là thư " +"mục) là « true » (đúng): vùng chọn phải chứa chỉ thư mục\n" +" • « isfile » (là tập tin) là « true » (đúng) và « isdir » (là thư mục) cũng " +"là « true » (đúng): vùng chọn có thể chứa cả tập tin lẫn thư mục đều\n" +" • « isfile » (là tập tin) là « false » (không đúng) và « isdir » (là thư " +"mục) cũng là « false » (không đúng): tổ hợp không hợp lệ." + +#: ../utils/nautilus-actions-convert.c:41 +msgid "The old xml config file to convert" +msgstr "Tập tin cấu hình XML cũ cần chuyển đổi" + +#: ../utils/nautilus-actions-convert.c:42 +msgid "The name of the newly-converted GConf schema file" +msgstr "Tên của tập tin giản đồ GConf mới được chuyển đổi" + +#: ../utils/nautilus-actions-convert.c:43 +msgid "Convert all old xml config files from previous installations [default]" +msgstr "" +"Chuyển đổi mọi tập tin cấu hình XML cũ từ bản cài đặt trước nào [mặc định]" + +#: ../utils/nautilus-actions-convert.c:44 +msgid "" +"The folder where the new GConf schema files will be saved if option -a is " +"set [default=/tmp]" +msgstr "" +"Thư mục nơi những tập tin giản đồ GConf mới sẽ được kưu nếu tùy chọn « -a » " +"được lập [mặc định=/tmp]" + +#: ../utils/nautilus-actions-convert.c:77 +#: ../utils/nautilus-actions-new-config.c:95 +#, c-format +msgid "" +"Syntax error:\n" +"\t- %s\n" +"Try %s --help\n" +msgstr "" +"Lỗi cú pháp:\n" +"\t- %s\n" +"Hãy chạy lệnh « %s --help » (trợ giúp)\n" + +#: ../utils/nautilus-actions-convert.c:83 +#, c-format +msgid "" +"Syntax error:\n" +"\tOptions -i and -o are mutually exclusive with option -a\n" +"Try %s --help\n" +msgstr "" +"Lỗi cú pháp:\n" +"\ttùy chọn « -i » và « -o » loại từ lẫn nhau với tùy chọn « -a »\n" +"Hãy chạy lệnh « %s --help » (trợ giúp)\n" + +#: ../utils/nautilus-actions-convert.c:89 +#, c-format +msgid "" +"Syntax error:\n" +"\tOption -i is mandatory when using option -o\n" +"Try %s --help\n" +msgstr "" +"Lỗi cú pháp\n" +"\ttùy chọn « -i » bắt buộc khi dùng tùy chọn « -o »\n" +"Hãy chạy lệnh « %s --help » (trợ giúp)\n" + +#: ../utils/nautilus-actions-convert.c:101 +#, c-format +msgid "" +"Error:\n" +"\t- Can't parse %s\n" +msgstr "" +"Lỗi: \n" +"\tKhông thể phân tách %s\n" + +#: ../utils/nautilus-actions-convert.c:115 +#, c-format +msgid "Converting %s ..." +msgstr "Đang chuyển đổi %s..." + +#: ../utils/nautilus-actions-convert.c:135 +#: ../utils/nautilus-actions-new-config.c:152 +#, c-format +msgid " Failed: Can't create %s : %s\n" +msgstr " Bị lỗi: không thể tạo %s: %s\n" + +#: ../utils/nautilus-actions-convert.c:143 +#: ../utils/nautilus-actions-new-config.c:160 +#, c-format +msgid " Ok, saved in %s\n" +msgstr " Được, đã lưu được vào %s\n" + +#: ../utils/nautilus-actions-convert.c:148 +#: ../utils/nautilus-actions-new-config.c:165 +#, c-format +msgid " Failed\n" +msgstr " Bị lỗi\n" + +#: ../utils/nautilus-actions-new-config.c:48 ../srcore/srpres.c:831 +msgid "LABEL" +msgstr "NHÃN" + +#: ../utils/nautilus-actions-new-config.c:49 +msgid "TOOLTIP" +msgstr "MẸO CÔNG CỤ" + +#: ../utils/nautilus-actions-new-config.c:50 +msgid "The icon of the menu item (filename or Gtk stock id)" +msgstr "Biểu tượng của mục trình đơn (tên tập tin hay ID GTK chuẩn)" + +#: ../utils/nautilus-actions-new-config.c:50 ../srcore/srpres.c:829 +msgid "ICON" +msgstr "BIỂU_TƯỢNG" + +#: ../utils/nautilus-actions-new-config.c:52 +msgid "PARAMS" +msgstr "THAM_SỐ" + +#: ../utils/nautilus-actions-new-config.c:53 +msgid "" +"A pattern to match selected files with possibility to add jokers ? or * (you " +"must set it for each pattern you need)" +msgstr "" +"Mẫu để khớp tập tin đã chọn, có thể thêm ký tự đặc biệt « ? » hay « * " +"» (phải lập cho mỗi mẫu cần thiết)" + +#: ../utils/nautilus-actions-new-config.c:53 +msgid "EXPR" +msgstr "BTHỨC" + +#: ../utils/nautilus-actions-new-config.c:54 +msgid "Set it if the selection can contain files" +msgstr "Lập nếu vùng chọn có thể chứa tập tin" + +#: ../utils/nautilus-actions-new-config.c:55 +msgid "Set it if the selection can contain folders" +msgstr "Lập nếu vùng chọn có thể chứa thư mục" + +#: ../utils/nautilus-actions-new-config.c:56 +msgid "Set it if the selection can have several items" +msgstr "Lập nếu vùng chọn có thể chứa vài mục" + +#: ../utils/nautilus-actions-new-config.c:57 +msgid "" +"A GnomeVFS scheme where the selected files should be located (you must set " +"it for each scheme you need)" +msgstr "" +"Lược đồ GnomeVFS nơi cần định vị những tập tin đã chọn (phải lập cho mỗi " +"lược đồ cần thiết)" + +#: ../utils/nautilus-actions-new-config.c:57 +msgid "SCHEME" +msgstr "LƯỢC ĐỒ" + +#: ../utils/nautilus-actions-new-config.c:58 +msgid "" +"The path of the file where to save the new GConf schema definition file " +"[default: /tmp/config_UUID.schemas]" +msgstr "" +"Đường dẫn của tập tin nơi cần lưu tập tin diễn tả giản đồ GConf mới [mặc " +"định: ]" + +#: ../utils/nautilus-actions-new-config.c:133 +#, c-format +msgid "Creating %s ..." +msgstr "Đang tạo %s..." + +#: ../utils/nautilus-actions-tools-utils.c:48 +#, c-format +msgid "Can't write data in file %s\n" +msgstr "Không thể ghi dữ liệu vào tập tin %s\n" + +#: ../utils/nautilus-actions-tools-utils.c:54 +#, c-format +msgid "Can't open file %s for writing\n" +msgstr "Không thể mở tập tin « %s » để ghi\n" + +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:70 +msgid "" +"The tooltip of the menu item that will appear in the Nautilus statusbar when " +"the user points to the Nautilus popup menu item with his/her mouse" +msgstr "" +"Mẹo công cụ của mục trình đơn sẽ xuất hiện trong thanh trạng thái khi người " +"dùng chỉ con chuột đến mục trình đơn bật lệnh Nautilus" + +#. i18n notes : Sorry for this long paragraph, will try to fix it the next release +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:77 +msgid "" +"The parameters of the command to start when the user selects the menu item " +"in the Nautilus popup menu.\n" +"\n" +"The parameters can contain some special tokens which are replaced by " +"Nautilus informations before starting the command :\n" +"\n" +"%d : base folder of the selected file(s)\n" +"%f : the name of the selected file or the 1st one if many are selected\n" +"%m : space-separated list of the basenames of the selected file(s)/folder" +"(s)\n" +"%M : space-separated list of the selected file(s)/folder(s), with their full " +"paths\n" +"%u : gnome-vfs URI\n" +"%s : scheme of the gnome-vfs URI\n" +"%h : hostname of the gnome-vfs URI\n" +"%U : username of the gnome-vfs URI\n" +"%% : a percent sign" +msgstr "" +"Những tham số của lệnh cần chạy khi người dùng chọn mục trình đơn trong " +"trình đơn bật lên Nautilus.\n" +"\n" +"Những tham số có thể chứa một số hiệu bài đặc biệt, mà được thay thế bằng " +"thông tin Nautilus trước khi khởi chạy lệnh:\n" +"\n" +" • %d\t— thư mục cơ bản của tập tin đã chọn\n" +" • %f\t\t— tên của tập tin đã chọn, hay điều thứ nhất nếu có nhiều tập tin\n" +" • %m\t— danh sách tên cơ bản của các tập tin/thư mục, định giới bằng dấu " +"cách\n" +" • %M\t— danh sách những tập tin/thư mục đã chọn với toàn đường dẫn định " +"giới bằng dấu cách\n" +" • %u\t— URI Gnome-VFS\n" +" • %s\t— lược đồ của URI Gnome-VFS\n" +" • %h\t— tên máy của URI Gnome-VFS\n" +" • %U\t— tên người dùng của URI Gnome-VFS\n" +" • %%\t— dấu phần trăm" + +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:78 +msgid "The list of pattern to match the selected file(s)/folder(s)" +msgstr "Danh sách các mẫu cần khớp với những tập tin/thư mục đã chọn" + +#. i18n notes : Sorry for this long paragraph, will try to fix it in the next release +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:81 +msgid "" +"Here are the valid combinations :\n" +"\n" +"- 'isfile' is 'true' and 'isdir' is 'false' : the selection must holds only " +"files\n" +"- 'isfile' is 'false' and 'isdir' is 'true' : the selection must holds only " +"folders\n" +"- 'isfile' is 'true' and 'isdir' is 'true' : the selection can holds both " +"files and folders\n" +"- 'isfile' is 'false' and 'isdir' is 'false' : invalid combination" +msgstr "" +"Những tổ hợp đúng:\n" +"\n" +"• « isfile » (là tập tin) là « true » (đúng) còn « isdir » (là thư mục) là « " +"false » (không đúng): vùng chọn phải chứa chỉ tập tin\n" +" • « isfile » (là tập tin) là « false » (không đúng) còn « isdir » (là thư " +"mục) là « true » (đúng): vùng chọn phải chứa chỉ thư mục\n" +" • « isfile » (là tập tin) là « true » (đúng) và « isdir » (là thư mục) cũng " +"là « true » (đúng): vùng chọn có thể chứa cả tập tin lẫn thư mục đều\n" +" • « isfile » (là tập tin) là « false » (không đúng) và « isdir » (là thư " +"mục) cũng là « false » (không đúng): tổ hợp không hợp lệ." + +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:82 +msgid "'true' if the selection can have files, 'false' otherwise" +msgstr "" +"« true » (đúng) nếu vùng chọn có thể chứa tập tin, không thì « false " +"» (không đúng)" + +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:83 +msgid "This setting is tied with the 'isdir' setting. " +msgstr "Thiết lập này liên quan đến thiết lập « isdir » (là thư mục) " + +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:84 +msgid "'true' if the selection can have folders, 'false' otherwise" +msgstr "" +"« true » (đúng) nếu vùng chọn có thể chứa thư mục, không thì « false " +"» (không đúng)" + +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:85 +msgid "This setting is tied with the 'isfile' setting. " +msgstr "Thiết lập này liên quan đến thiết lập « isfile » (là tập tin) " + +#. i18n notes : Sorry for this long paragraph, will try to fix it in the next release +#: ../libnautilus-actions/nautilus-actions-config-gconf-private.h:90 +msgid "" +"Defines the list of valid GnomeVFS schemes to be matched with the selected " +"items. The GnomeVFS scheme is the protocol used to access the files. The " +"keyword to use is the one used in the GnomeVFS URI.\n" +"\n" +"Example of GnomeVFS URI : \n" +"file:///tmp/foo.txt\n" +"sftp:///root@test.example.net/tmp/foo.txt\n" +"\n" +"The most common schemes are :\n" +"\n" +"'file' : local files\n" +"'sftp' : files accessed via SSH\n" +"'ftp' : files accessed via FTP\n" +"'smb' : files accessed via Samba (Windows share)\n" +"'dav' : files accessed via WebDav\n" +"\n" +"All GnomeVFS schemes used by Nautilus can be used here." +msgstr "" +"Định nghĩa danh sách lược đồ GnomeVFS hợp lệ để khớp những mục đã chọn. Lược " +"đồ GnomeVFS là giao thức được dùng để truy cập những tập tin. Từ khoá cần " +"dùng là điều dùng trong URI GnomeVFS (v.d. ). Những lược đồ thường nhất:\n" +" • file \t— tập tin cục bộ\n" +" • sftp \t— tập tin được truy cập bằng SSH\n" +" • ftp \t— tập tin được truy cập bằng FTP\n" +" • smb\t— tập tin được truy cập bằng Samba (chia sẻ Windows)\n" +" • dav\t— tập tin được truy cập bằng WebDav.\n" +"Ở đây có thể sử dụng lược đồ nào do Nautilus dùng." + +#: ../admin-tool/aboutdialog.py:63 +msgid "Program to establish and edit profiles for users" +msgstr "Chương trình tạo và sửa đổi tiểu sử sơ lược cho người dùng" + +#: ../admin-tool/changeswindow.py:50 +#, python-format +msgid "Changes in profile %s" +msgstr "Các thay đổi tiểu sử sơ lược %s" + +#: ../apt-mirror-setup.templates:32 src/common/xchat.c:834 ../hwconf.c:1728 +msgid "Ignore" +msgstr "Bỏ qua" + +#: ../sheets/ciscomisc.sheet.in.h:19 +msgid "Lock" +msgstr "Khoá" + +#: ../admin-tool/editorwindow.py:97 +#, python-format +msgid "Profile %s" +msgstr "Tiểu sử sơ lược %s" + +#: ../admin-tool/editorwindow.py:189 ../admin-tool/sessionwindow.py:174 +msgid "_Profile" +msgstr "Tiểu sử s_ơ lược" + +#: ../admin-tool/editorwindow.py:190 ../admin-tool/sessionwindow.py:175 +msgid "Save profile" +msgstr "Lưu tiểu sử sơ lược" + +#: ../admin-tool/editorwindow.py:191 ../admin-tool/sessionwindow.py:176 +msgid "Close the current window" +msgstr "Đóng cửa sổ hiện có" + +#: ../admin-tool/editorwindow.py:193 ../structure.c:248 +msgid "Delete item" +msgstr "Xoá bỏ mục" + +#: ../admin-tool/editorwindow.py:195 ../admin-tool/sessionwindow.py:181 +msgid "About Sabayon" +msgstr "Giới thiệu Sabayon" + +#: ../admin-tool/editorwindow.py:254 ../lib/sources/gconfsource.py:123 +msgid "GConf" +msgstr "GConf" + +#: ../gtk/gtkfilesel.c:763 ../src/prefs.c:597 ../pan/gui.c:1169 +#: ../pan/save-ui.c:240 +msgid "Files" +msgstr "Tập tin" + +#: ../admin-tool/fileviewer.py:29 +#, python-format +msgid "Profile file: %s" +msgstr "Tập tin tiểu sử sơ lược: %s" + +#: ../admin-tool/gconfviewer.py:59 ../admin-tool/gconfviewer.py:78 +msgid "" +msgstr "" + +#: ../admin-tool/gconfviewer.py:60 +msgid "" +msgstr "" + +#: ../admin-tool/gconfviewer.py:64 common/config.cpp:73 +#: ogg123/cfgfile_options.c:174 +msgid "string" +msgstr "chuỗi" + +#: ../admin-tool/gconfviewer.py:66 common/config.cpp:73 +msgid "integer" +msgstr "số nguyên" + +#: ../admin-tool/gconfviewer.py:68 ogg123/cfgfile_options.c:180 +msgid "float" +msgstr "nổi" + +#: ../admin-tool/gconfviewer.py:70 common/config.cpp:73 +msgid "boolean" +msgstr "bun" + +#: ../admin-tool/gconfviewer.py:72 +msgid "schema" +msgstr "giản đồ" + +#: ../addressbook/gui/widgets/eab-gui-util.c:588 ../atk/atkobject.c:113 +#: ../src/orca/rolenames.py:299 +msgid "list" +msgstr "danh sách" + +#: ../admin-tool/gconfviewer.py:76 +msgid "pair" +msgstr "cặp" + +#: ../admin-tool/gconfviewer.py:88 +#, python-format +msgid "Profile settings: %s" +msgstr "Thiết lập tiểu sử sơ lược: %s" + +#: ../plug-ins/gimpressionist/size.c:141 ../plug-ins/metadata/interface.c:142 +#: ../objects/UML/umlattribute.c:41 ../objects/UML/umlparameter.c:47 +#: app/envelope-box.c:881 +msgid "Value" +msgstr "Giá trị" + +#, c-format, python-format +msgid "%s (%s)" +msgstr "%s (%s)" + +#: ../admin-tool/sabayon:44 +msgid "" +"Your account does not have permissions to run the Desktop User Profiles tool" +msgstr "" +"Tài khoản của bạn không có quyền chạy công cụ Tiểu sử sơ lược Người dùng Môi " +"trường" + +#: ../admin-tool/sabayon:45 +msgid "" +"Administrator level permissions are needed to run this program because it " +"can modify system files." +msgstr "" +"Cần thiết quyền lớp quản trị để chạy chương trình này vì nó có thể sửa đổi " +"tập tin hệ thống." + +#: ../admin-tool/sabayon:50 +msgid "Desktop User Profiles tool is already running" +msgstr " Công cụ Tiểu sử sơ lược Người dùng Môi trường đang chạy" + +#: ../admin-tool/sabayon:51 +msgid "" +"You may not use Desktop User Profiles tool from within a profile editing " +"session" +msgstr "" +"Bạn không thể sử dụng công cụ Tiểu sử sơ lược Người dùng Môi trường ở trong " +"phiên hiệu chỉnh tiểu sử sơ lược" + +#: ../admin-tool/sabayon:58 +#, c-format +msgid "User account '%s' was not found" +msgstr "Không tìm thấy tài khoản người dùng « %s »" + +#: ../admin-tool/sabayon:59 +#, c-format +msgid "" +"Sabayon requires a special user account '%s' to be present on this computer. " +"Try again after creating the account (using, for example, the 'adduser' " +"command)" +msgstr "" +"Sabayon cần thiết một tài khoản người dùng đặc biệt « %s » có trong máy tính " +"này. Hãy thử lại sau khi tạo tài khoản này (lấy thí dụ, bằng lệnh « adduser " +"» [thêm người dùng])" + +#: ../admin-tool/sabayon-apply:39 +#, c-format +msgid "No profile for user '%s' found\n" +msgstr "Không tìm thấy tiểu sử sơ lược cho người dùng « %s »\n" + +#: ../admin-tool/sabayon-apply:44 +#, c-format +msgid "Usage: %s []\n" +msgstr "Cách sử dụng: %s []\n" + +#: ../admin-tool/sabayon-session:32 +#, c-format +msgid "Usage: %s \n" +msgstr "" +"Cách sử dụng: %s <đường_dẫn_tiểu_sử_sơ_lược> " +"\n" + +#: ../admin-tool/sabayon.desktop.in.h:1 +msgid "Establish and Edit Profiles for Users" +msgstr "Tạo và Sửa đổi Tiểu sử sơ lược cho Người dùng" + +#: ../admin-tool/sabayon.desktop.in.h:2 ../admin-tool/sabayon.glade.h:4 +msgid "User Profile Editor" +msgstr "Bộ Sửa đổi Tiểu sử sơ lược Người dùng" + +#: ../admin-tool/sabayon.glade.h:1 +msgid "Add Profile" +msgstr "Thêm tiểu sử sơ lược" + +#: ../admin-tool/sabayon.glade.h:2 +msgid "Profile _name:" +msgstr "T_ên tiểu sử sơ lược:" + +#: ../admin-tool/sabayon.glade.h:3 +msgid "Use this profile for _all users" +msgstr "Dùng tiểu sử sơ lược này cho _mọi người dùng" + +#: ../admin-tool/sabayon.glade.h:5 ../src/gnome-terminal.glade2.h:92 +msgid "_Base on:" +msgstr "_Dựa trên:" + +#: ../admin-tool/sabayon.glade.h:6 ../profiles/audio-profiles-edit.c:650 +#: ../src/terminal.c:3037 +msgid "_Profiles:" +msgstr "_Hồ sơ :" + +#: ../admin-tool/sabayon.glade.h:7 ../glom/application.cc:283 +msgid "_Users" +msgstr "_Người dùng" + +#: ../admin-tool/sabayon.glade.h:8 +msgid "_Users:" +msgstr "_Người dùng:" + +#: ../admin-tool/saveconfirm.py:36 +msgid "Close _Without Saving" +msgstr "Đóng mà _không lưu" + +#: ../admin-tool/saveconfirm.py:43 +#, python-format +msgid "Save changes to profile \"%s\" before closing?" +msgstr "Lưu các thay đổi tiểu sử sơ lược « %s » trước khi đóng không?" + +#: ../admin-tool/saveconfirm.py:47 +#: ../gedit/dialogs/gedit-close-confirmation-dialog.c:367 +#, c-format, python-format +msgid "" +"If you don't save, changes from the last %ld second will be permanently lost." +msgid_plural "" +"If you don't save, changes from the last %ld second will be permanently lost." +msgstr[0] "" +"Nếu bạn không lưu, các thay đổi của bạn ở %ld phút chót sẽ bị mất hoàn toàn." + +#: ../admin-tool/saveconfirm.py:53 +#: ../gedit/dialogs/gedit-close-confirmation-dialog.c:376 +msgid "" +"If you don't save, changes from the last minute will be permanently lost." +msgstr "" +"Nếu bạn không lưu, các thay đổi của bạn ở phút chót sẽ bị mất hoàn toàn." + +#: ../admin-tool/saveconfirm.py:57 +#: ../gedit/dialogs/gedit-close-confirmation-dialog.c:382 +#, c-format, python-format +msgid "" +"If you don't save, changes from the last minute and %ld second will be " +"permanently lost." +msgid_plural "" +"If you don't save, changes from the last minute and %ld second will be " +"permanently lost." +msgstr[0] "" +"Nếu bạn không lưu, các thay đổi của bạn ở phút chót và %ld giây sẽ bị mất " +"hoàn toàn." + +#: ../admin-tool/saveconfirm.py:64 +#: ../gedit/dialogs/gedit-close-confirmation-dialog.c:392 +#, c-format, python-format +msgid "" +"If you don't save, changes from the last %ld minute will be permanently lost." +msgid_plural "" +"If you don't save, changes from the last %ld minute will be permanently lost." +msgstr[0] "" +"Nếu bạn không lưu, các thay đổi của bạn ở %ld phút chót sẽ bị mất hoàn toàn." + +#: ../gedit/dialogs/gedit-close-confirmation-dialog.c:428 +#, c-format, python-format +msgid "If you don't save, changes from the last hour will be permanently lost." +msgid_plural "" +"If you don't save, changes from the last hour will be permanently lost." +msgstr[0] "" +"Nếu bạn không lưu, các thay đổi của bạn ở giờ sau chót sẽ bị mất hoàn toàn." + +#: ../admin-tool/saveconfirm.py:76 +#: ../gedit/dialogs/gedit-close-confirmation-dialog.c:413 +#, c-format, python-format +msgid "" +"If you don't save, changes from the last hour and %d minute will be " +"permanently lost." +msgid_plural "" +"If you don't save, changes from the last hour and %d minute will be " +"permanently lost." +msgstr[0] "" +"Nếu bạn không lưu, các thay đổi của bạn ở giờ sau chót và %d phút chót sẽ bị " +"mất hoàn toàn." + +#: ../admin-tool/sessionwindow.py:153 +#, python-format +msgid "Editing profile %s" +msgstr "Đang sửa đổi tiểu sử sơ lược %s" + +#: src/gtkam-main.c:556 ../src/mud-tray.c:206 src/docklet.cpp:120 +#: src/mainwin.cpp:567 po/silky.glade.h:219 app/menubar.c:428 +msgid "_Quit" +msgstr "T_hoát" + +#: ../admin-tool/sessionwindow.py:178 +msgid "_Changes" +msgstr "_Đổi" + +#: ../admin-tool/sessionwindow.py:178 +msgid "Edit changes" +msgstr "Sửa đổi các thay đổi" + +#: ../admin-tool/sessionwindow.py:179 +msgid "_Lockdown" +msgstr "_Khoá xuống" + +#: ../admin-tool/sessionwindow.py:179 +msgid "Edit Lockdown settings" +msgstr "Sửa đổi thiết lập Khoá xuống" + +#: ../admin-tool/sessionwindow.py:184 +msgid "Enforce Mandatory" +msgstr "Ép làm việc Bắt buộc" + +#: ../admin-tool/sessionwindow.py:184 +msgid "Enforce mandatory settings in the editing session" +msgstr "Ép làm dùng thiết lập bắt buộc trong phiên sửa đổi" + +#: ../admin-tool/sessionwindow.py:273 +#, python-format +msgid "Lockdown settings for %s" +msgstr "Thiết lập khoá xuống cho %s" + +#: ../admin-tool/usersdialog.py:67 +#, python-format +msgid "Users for profile %s" +msgstr "Người dùng cho tiểu sử sơ lược %s" + +#: ../admin-tool/usersdialog.py:95 +msgid "Use This Profile" +msgstr "Dùng Tiểu sử sơ lược này" + +#: ../lib/dirmonitor.py:151 +#, python-format +msgid "Failed to add monitor for %s" +msgstr "Việc thêm thiết bị hiển thị cho %s bị lỗi" + +#: ../lib/dirmonitor.py:231 +#, python-format +msgid "Expected event: %s %s" +msgstr "Sự kiện mong đợi: %s %s" + +#: ../lib/protosession.py:142 +msgid "Unable to find a free X display" +msgstr "Không tìm thấy bộ trình bày X còn rảnh" + +#: ../lib/protosession.py:409 +msgid "Failed to start Xnest: timed out waiting for USR1 signal" +msgstr "Lỗi khởi chạy Xnest: quá giờ trong khi đợi ký hiệu USR1" + +#: ../lib/protosession.py:411 +msgid "Failed to start Xnest: died during startup" +msgstr "Lỗi khởi chạy Xnest: kết thúc trong khi khởi chạy" + +#: ../lib/sources/filessource.py:68 +#, python-format +msgid "File '%s' created" +msgstr "Tập tin « %s » đã được tạo" + +#: ../lib/sources/filessource.py:70 +#, python-format +msgid "File '%s' deleted" +msgstr "Tập tin « %s » bị xoá bỏ" + +#: ../lib/sources/filessource.py:72 +#, python-format +msgid "File '%s' changed" +msgstr "Tập tin « %s » đã được thay đổi" + +#: ../lib/sources/filessource.py:96 +msgid "Applications menu" +msgstr "Trình đơn Ứng dụng" + +#: ../lib/sources/filessource.py:98 +msgid "Preferences menu" +msgstr "Trình đơn Tùy thích" + +#: ../lib/sources/filessource.py:100 +msgid "Server Settings menu" +msgstr "Trình đơn Thiết lập Trình phục vụ" + +#: ../lib/sources/filessource.py:102 +msgid "System Settings menu" +msgstr "Trình đơn Thiết lập Hệ thống" + +#: ../lib/sources/filessource.py:104 +msgid "Start Here menu" +msgstr "Trình đơn Bắt đầu từ đây" + +#: ../lib/sources/gconfsource.py:89 +#, python-format +msgid "GConf key '%s' unset" +msgstr "Khoá GConf « %s » chưa lập" + +#: ../lib/sources/gconfsource.py:91 +#, python-format +msgid "GConf key '%s' set to string '%s'" +msgstr "Khoá GConf « %s » được đặt là chuỗi « %s »" + +#: ../lib/sources/gconfsource.py:93 +#, python-format +msgid "GConf key '%s' set to integer '%s'" +msgstr "Khoá GConf « %s » được đặt là số nguyên « %s »" + +#: ../lib/sources/gconfsource.py:95 +#, python-format +msgid "GConf key '%s' set to float '%s'" +msgstr "Khoá GConf « %s » được đặt là nổi « %s »" + +#: ../lib/sources/gconfsource.py:97 +#, python-format +msgid "GConf key '%s' set to boolean '%s'" +msgstr "Khoá GConf « %s » được đặt là bun « %s »" + +#: ../lib/sources/gconfsource.py:99 +#, python-format +msgid "GConf key '%s' set to schema '%s'" +msgstr "Khoá GConf « %s » được đặt là giản đồ « %s »" + +#: ../lib/sources/gconfsource.py:101 +#, python-format +msgid "GConf key '%s' set to list '%s'" +msgstr "Khoá GConf « %s » được đặt là danh sác h « %s »" + +#: ../lib/sources/gconfsource.py:103 +#, python-format +msgid "GConf key '%s' set to pair '%s'" +msgstr "Khoá GConf « %s » được đặt là cặp « %s »" + +#: ../lib/sources/gconfsource.py:105 +#, python-format +msgid "GConf key '%s' set to '%s'" +msgstr "Khoá GConf « %s » được đặt là « %s »" + +#: ../lib/sources/gconfsource.py:136 +msgid "Default GConf settings" +msgstr "Thiết lập GConf mặc định" + +#: ../lib/sources/gconfsource.py:138 +msgid "Mandatory GConf settings" +msgstr "Thiết lập GConf bắt buộc" + +#: ../lib/sources/mozillasource.py:132 +#, python-format +msgid "Mozilla key '%s' set to '%s'" +msgstr "Khoá Mozilla « %s » đã đặt là « %s »" + +#: ../lib/sources/mozillasource.py:134 +#, python-format +msgid "Mozilla key '%s' unset" +msgstr "Khoá Mozilla « %s » chưa đặt" + +#: ../lib/sources/mozillasource.py:136 +#, python-format +msgid "Mozilla key '%s' changed to '%s'" +msgstr "Khoá Mozilla « %s » được thay đổi thành « %s »" + +#: ../lib/sources/mozillasource.py:165 ../lib/sources/mozillasource.py:175 +msgid "Web browser preferences" +msgstr "Tùy trích trình duyệt Mạng" + +#: ../lib/sources/mozillasource.py:167 ../lib/sources/mozillasource.py:177 +msgid "Web browser bookmarks" +msgstr "Đánh dấu trình duyệt Mạng" + +#: ../lib/sources/mozillasource.py:169 +msgid "Web browser profile list" +msgstr "Danh sách tiểu sử sơ lược trình duyệt Mạng" + +#: ../lib/sources/mozillasource.py:520 +#, python-format +msgid "File Not Found (%s)" +msgstr "Không tìm thấy tập tin (%s)" + +#: ../lib/sources/mozillasource.py:871 +#, python-format +msgid "duplicate name(%(name)s) in section %(section)s" +msgstr "tên trùng(%(name)s) trong phần %(section)s" + +#: ../lib/sources/mozillasource.py:880 +#, python-format +msgid "redundant default in section %s" +msgstr "mặc định thừa trong phần %s" + +#: ../lib/sources/mozillasource.py:897 +msgid "no default profile" +msgstr "không có tiểu sử sơ lược mặc định" + +#: ../lib/sources/mozillasource.py:952 +#, python-format +msgid "Mozilla bookmark created '%s' -> '%s'" +msgstr "Đánh dấu Mozilla đã được tạo « %s » → « %s »" + +#: ../lib/sources/mozillasource.py:954 +#, python-format +msgid "Mozilla bookmark folder created '%s'" +msgstr "Thư mục đánh dấu Mozilla đã được tạo « %s »" + +#: ../lib/sources/mozillasource.py:957 +#, python-format +msgid "Mozilla bookmark deleted '%s'" +msgstr "Đánh dấu Mozilla bị xoá bỏ « %s »" + +#: ../lib/sources/mozillasource.py:959 +#, python-format +msgid "Mozilla bookmark folder deleted '%s'" +msgstr "Thư mục đánh dấu Mozilla bị xoá bỏ « %s »" + +#: ../lib/sources/mozillasource.py:962 +#, python-format +msgid "Mozilla bookmark changed '%s' '%s'" +msgstr "Đánh dấu Mozilla đã được thay đổi « %s » « %s »" + +#: ../lib/sources/mozillasource.py:964 +#, python-format +msgid "Mozilla bookmark folder changed '%s'" +msgstr "Thư mục đánh dấu Mozilla đã được thay đổi « %s »" + +#: ../lib/sources/paneldelegate.py:58 +#, python-format +msgid "Panel '%s' added" +msgstr "Bảng điều khiển « %s » đã được thêm" + +#: ../lib/sources/paneldelegate.py:64 +#, python-format +msgid "Panel '%s' removed" +msgstr "Bảng điều khiển « %s » bị gỡ bỏ" + +#: ../lib/sources/paneldelegate.py:70 +#, python-format +msgid "Panel applet '%s' added" +msgstr "Tiểu dụng bảng điều khiển « %s » đã được thêm" + +#: ../lib/sources/paneldelegate.py:76 +#, python-format +msgid "Panel applet '%s' removed" +msgstr "Tiểu dụng bảng điều khiển « %s » bị gỡ bỏ" + +#: ../lib/sources/paneldelegate.py:82 +#, python-format +msgid "Panel object '%s' added" +msgstr "Đối tượng Bảng điều khiển « %s » đã được thêm" + +#: ../lib/sources/paneldelegate.py:94 +#, python-format +msgid "Panel object '%s' removed" +msgstr "Đối tượng Bảng điều khiển « %s » bị gỡ bỏ" + +#: ../lib/sources/paneldelegate.py:375 +msgid "Panel File" +msgstr "Tập tin Bảng điều khiển" + +#: ../lib/storage.py:173 +#, python-format +msgid "Failed to read file '%s': %s" +msgstr "Lỗi đọc tập tin « %s »: %s" + +#: ../lib/storage.py:183 +#, python-format +msgid "Failed to read metadata from '%s': %s" +msgstr "Lỗi đọc siêu dữ liệu từ « %s »: %s" + +#: ../lib/storage.py:189 +#, python-format +msgid "Invalid metadata section in '%s': %s" +msgstr "Phần siêu dữ liệu không hợp lệ trong « %s »: %s" + +#: ../lib/storage.py:363 +#, python-format +msgid "Cannot add non-existent file '%s'" +msgstr "Không thể thêm tập tin không tồn tại « %s »" + +#: ../lib/storage.py:540 +#, python-format +msgid "Profile is read-only %s" +msgstr "Tiêu sử sơ lược chỉ cho phép đọc %s" + +#: ../lib/unittests.py:38 ../lib/unittests.py:39 +msgid "Ignore WARNINGs" +msgstr "Bỏ qua các CẢNH BÁO" + +#: ../lib/unittests.py:61 +#, python-format +msgid "Running %s tests" +msgstr "Đang chạy %s việc thử" + +#: ../lib/unittests.py:63 +#, python-format +msgid "Running %s tests (%s)" +msgstr "Đang chạy %s việc thử (%s)" + +#: ../lib/unittests.py:70 ../ui/welcome.glade.h:33 +msgid "Success!" +msgstr "• Thành công •" + +#: ../lib/userdb.py:52 +#, python-format +msgid "invalid type for setting %s in %s" +msgstr "kiểu không hợp lệ cho thiết lập %s trong %s" + +#: ../lib/userdb.py:212 +#, python-format +msgid "No search based specified for %s" +msgstr "Chưa ghi rõ cơ bản tìm kiếm cho %s" + +#: ../lib/userdb.py:215 +#, python-format +msgid "No query filter specified for %s" +msgstr "Chưa ghi rõ bộ lọc truy vấn cho %s" + +#: ../lib/userdb.py:218 +#, python-format +msgid "No result attribute specified for %s" +msgstr "Chưa ghi rõ thuộc tính kết quả cho %s" + +#: ../lib/userdb.py:227 +msgid "Scope must be one of sub, base and one" +msgstr "" +"Phạm vị phải là một của:\n" +" • sub (dưới)\n" +" • base (cơ bản)\n" +" • one (một)" + +#: ../lib/userdb.py:247 +msgid "multiple_result must be one of first and random" +msgstr "" +"multiple_result (nhiều kết quả) phải là một của:\n" +" • first (thứ nhất)\n" +" • random (ngẫu nhiên)" + +#: ../lib/userdb.py:339 +#, python-format +msgid "Could not open %s for writing" +msgstr "Không thể mở %s để ghi" + +#: ../lib/userdb.py:352 +#, python-format +msgid "Failed to save UserDatabase to %s" +msgstr "Lỗi lưu UserDatabase (cơ sở dữ liệu người dùng) vào %s" + +#: ../lib/userdb.py:375 ../lib/userdb.py:410 +#, python-format +msgid "File %s is not a profile configuration" +msgstr "Tập tin %s không phải là cấu hình tiểu sử sơ lược" + +#: ../lib/userdb.py:382 +#, python-format +msgid "Failed to add default profile %s to configuration" +msgstr "Lỗi thêm tiểu sử sơ lược mặc định %s vào cấu hình" + +#: ../lib/userdb.py:418 +#, python-format +msgid "Failed to add user %s to profile configuration" +msgstr "Lỗi thêm người dùng %s vào cấu hình tiểu sử sơ lược" + +#: ../lib/userdb.py:449 +msgid "Failed to get the user list" +msgstr "Lỗi lấy danh sách người dùng" + +#: ../lib/util.py:127 +msgid "" +"Cannot find home directory: not set in /etc/passwd and no value for $HOME in " +"environment" +msgstr "" +"Không tìm thấy thư mục chính: chưa lập trong và không có giá " +"trị cho biến « $HOME » trong môi trường." + +#: ../lib/util.py:140 +msgid "" +"Cannot find username: not set in /etc/passwd and no value for $USER in " +"environment" +msgstr "" +"Không tìm thấy tên người dùng: chưa lập trong và không có giá " +"trị cho biến « $USER » trong môi trường." + +#: src/filehandling_functions.c:469 src/filehandling_functions.c:476 +#, c-format +msgid "Searching for indirect done" +msgstr "Đang tìm kiếm xong gián tiếp" + +#: src/filehandling_functions.c:508 src/filehandling_functions.c:515 +#, c-format +msgid "Warning: could not find tag table" +msgstr "Cảnh báo : không tìm thấy bảng thẻ" + +#: src/filehandling_functions.c:542 +#, c-format +msgid "Searching for tag table done\n" +msgstr "Đang tìm kiếm xong bảng thẻ\n" + +#: src/filehandling_functions.c:1211 +#, c-format +msgid "Error: could not open info file\n" +msgstr "Lỗi: không thể mở tập tin thông tin\n" + +#: src/mainfunction.c:143 src/manual.c:975 +msgid "Are you sure you want to print?" +msgstr "Bạn có chắc muốn in không?" + +#: src/mainfunction.c:195 src/manual.c:1020 +msgid "Enter line: " +msgstr "Gõ dòng: " + +#: src/mainfunction.c:236 src/manual.c:1067 +msgid "Enter command: " +msgstr "Gõ lệnh: " + +#: src/mainfunction.c:255 +msgid "Operation failed..." +msgstr "Thao tác thất bại..." + +#: src/mainfunction.c:291 src/mainfunction.c:551 src/manual.c:1120 +msgid "Enter regular expression: " +msgstr "Gõ biểu thức chính quy: " + +#: src/mainfunction.c:521 src/mainfunction.c:615 src/manual.c:1196 +msgid "Search string not found..." +msgstr "Không tìm thấy chuỗi tìm kiếm..." + +#: src/mainfunction.c:576 src/manual.c:1153 +msgid "Invalid regular expression;" +msgstr "Biểu thức chính quy không hợp lệ;" + +#: src/mainfunction.c:578 src/manual.c:1155 +msgid "Press any key to continue..." +msgstr "Bấm phím nào để tiếp tục..." + +#: src/mainfunction.c:644 +msgid "Enter node name: " +msgstr "Gõ tên nút: " + +#: src/mainfunction.c:720 +#, c-format +msgid "Node %s not found" +msgstr "Không tìm thấy nút %s" + +#: src/mainfunction.c:1178 src/manual.c:1546 ../src/red_transaction.py:96 +#: ../glade/glade_project_window.c:1698 ../glade/glade_project_window.c:1704 +msgid "Are you sure you want to quit?" +msgstr "Bạn có chắc muốn thoát không?" + +#: src/manual.c:315 +#, c-format +msgid "Error: Cannot call man command.\n" +msgstr "Lỗi: không thể gọi lệnh man (hướng dẫn).\n" + +#: src/manual.c:324 +#, c-format +msgid "Error: No manual page found either.\n" +msgstr "Lỗi: cũng không tìm thấy trang hướng dẫn.\n" + +#: src/manual.c:327 +#, c-format +msgid "Apropos pages:\n" +msgstr "Trang Apropos:\n" + +#: src/manual.c:370 +msgid "Calling gunzip for" +msgstr "Đang gọi gunzip cho" + +#: src/manual.c:376 +#, c-format +msgid "Couldn't call gunzip.\n" +msgstr "Không thể gọi gunzip.\n" + +#: src/manual.c:413 +msgid "IGNORING" +msgstr "ĐANG BỎ QUA" + +#: src/manual.c:456 +#, c-format +msgid "Error: No manual page found\n" +msgstr "Lỗi: không tìm thấy trang hướng dẫn\n" + +#: src/manual.c:461 +#, c-format +msgid "Calling apropos \n" +msgstr "Đang gọi apropos \n" + +#: src/manual.c:466 +#, c-format +msgid "Nothing appropiate\n" +msgstr "Không có gì thích hợp\n" + +#: src/manual.c:989 +msgid "Enter manual name: " +msgstr "Gõ tên sổ hướng dẫn: " + +#: src/manual.c:1629 src/video.c:114 +#, c-format +msgid "Viewing line %d/%d, %d%%" +msgstr "Đang xem dòng %d/%d, %d%%" + +#: src/manual.c:1631 src/video.c:116 +#, c-format +msgid "Viewing line %d/%d, 100%%" +msgstr "Đang xem dòng %d/%d, 100%%" + +#: src/parse_config.c:113 +#, c-format +msgid "Can't open config file!\n" +msgstr "• Không thể mở tập tin cấu hình. •\n" + +#: src/parse_config.c:163 +#, c-format +msgid "Parse error in config file on line %d\n" +msgstr "Gặp lỗi phân tách trong tập tin cấu hình trên dòng %d\n" + +#: src/utils.c:122 src/utils.c:178 +#, c-format +msgid "Virtual memory exhausted\n" +msgstr "Hết bộ nhớ ảo\n" + +#: src/utils.c:232 +#, c-format +msgid "" +"Illegal characters in filename!\n" +"*** %s\n" +msgstr "" +"• Gặp ký tự sai trong tên tập tin. •\n" +"*** %s\n" + +#: ../partman-basicfilesystems.templates:113 src/shar.c:679 utils.c:121 +#: utils.c:127 utils.c:133 utils.c:139 utils.c:145 utils.c:151 utils.c:157 +#: ../mimedir/mimedir-vcard.c:3665 +msgid "yes" +msgstr "có" + +#: ../partman-basicfilesystems.templates:118 src/shar.c:680 dir.c:1035 +#: dir.c:1056 utils.c:123 utils.c:129 utils.c:135 utils.c:141 utils.c:147 +#: utils.c:153 utils.c:159 +msgid "no" +msgstr "không" + +#: src/video.c:61 src/fe-gtk/dccgui.c:351 ../glade/glade_project_options.c:743 +#: ../glade/gnome/gnomepixmap.c:79 +msgid "File:" +msgstr "Tập tin:" + +#: src/video.c:62 +msgid "Node:" +msgstr "Nút:" + +#: src/video.c:63 ../glines/glines.c:1937 makeinfo/node.c:991 +msgid "Next:" +msgstr "Kế:" + +#: src/video.c:64 +msgid "Prev:" +msgstr "Trước:" + +#: src/video.c:65 ../directed.xml.in.h:16 makeinfo/node.c:1021 +msgid "Up:" +msgstr "Lên:" + +#: src/pinfo.c:113 src/pinfo.c:198 +#, c-format +msgid "Looking for man page...\n" +msgstr "Đang tìm trang hướng dẫn...\n" + +#: src/pinfo.c:151 +#, c-format +msgid "--node option used without argument\n" +msgstr "Tùy chọn « --node » (nút) được dùng không có đối số\n" + +#: src/pinfo.c:161 +#, c-format +msgid "--rcfile option used without argument\n" +msgstr "Tùy chọn « --rcfile » (tập tin rc) được dùng không có đối số\n" + +#: src/pinfo.c:172 +#, c-format +msgid "" +"Usage:\n" +"%s [options] [info|manual]\n" +"Options:\n" +"-h, --help help\n" +"-v, --version version\n" +"-m, --manual use man page\n" +"-r, --raw-filename use raw filename\n" +"-f, --file synonym for -r\n" +"-a, --apropos call apropos if nothing found\n" +"-p, --plain-apropos call only apropos\n" +"-c, --cut-man-headers cut out repeated man headers\n" +"-l, --long-manual-links use long link names in manuals\n" +"-s, --squeeze-manlines cut empty lines from manual pages\n" +"-d, --dont-handle-without-tag-table don't display texinfo pages without " +"tag\n" +" tables\n" +"-t, --force-manual-tag-table force manual detection of tag table\n" +"-x, --clear-at-exit clear screen at exit\n" +" --node=nodename, --node nodename jump directly to the node nodename\n" +" --rcfile=file, --rcfile file use alternate rcfile\n" +msgstr "" +"Cách sử dụng:\n" +"%s [tùy_chọn ...] [thông_tin|sổ_hướng_dẫn]\n" +"Options:\n" +"-h, --help _trợ giúp_\n" +"-v, --version _phiên bản_\n" +"-m, --manual sử dụng _trang hướng dẫn_\n" +"-r, --raw-filename sử dụng _tên tập tin thô_\n" +"-f, --file bằng tùy chọn « -r » (_tập tin_)\n" +"-a, --apropos gọi apropos nếu không tìm gì\n" +"-p, --plain-apropos gọi chỉ apropos thôi (_chuẩn_)\n" +"-c, --cut-man-headers _cắt ra các dòng đầu hướng dẫn_ trùng\n" +"-l, --long-manual-links sử dụng tên _liên kết dài_ trong _sổ " +"hướng dẫn_\n" +"-s, --squeeze-manlines cắt các _dòng_ trắng ra trang _hướng " +"dẫn_ (_vắt_)\n" +"-d, --dont-handle-without-tag-table _đừng_ hiển thị trang kiểu texinfo\n" +"\t\t\t\t\t\t\t\t_không có bảng thẻ_ (_quản " +"lý_) -t, --force-manual-tag-" +"table _buộc_ tự phát hiện _bảng thẻ_\n" +"-x, --clear-at-exit _xoá_ màn hình _khi thoát_\n" +" --node=nodename, --node nodename nhảy thẳng đến _nút tên_ này\n" +" --rcfile=tập_tin, --rcfile tập_tin sử dụng tập tin rc thay thế\n" + +#: src/pinfo.c:312 +#, c-format +msgid "Error: could not open info file, trying manual\n" +msgstr "Lỗi: không thể mở tập tin thông tin nên thử sổ hướng dẫn\n" + +#: src/pinfo.c:345 +#, c-format +msgid "Warning: tag table not found...\n" +msgstr "Cảnh báo : không tìm thấy bảng thẻ...\n" + +#: src/pinfo.c:349 +#, c-format +msgid "Trying to create alternate tag table...\n" +msgstr "Đang cố tạo bảng thẻ thay thế...\n" + +#: src/pinfo.c:354 src/pinfo.c:564 +#, c-format +msgid "This doesn't look like info file...\n" +msgstr "Điều này không hình như tập tin thông tin...\n" + +#: src/pinfo.c:367 +#, c-format +msgid "Specified node does not exist...\n" +msgstr "Không có nút đã gõ...\n" + +#: src/pinfo.c:419 +msgid "Tag table is corrupt, trying to fix..." +msgstr "Bảng thẻ bị hỏng nên cố sửa..." + +#: src/pinfo.c:420 +msgid "press a key to continue" +msgstr "bấm phím nào để tiếp tục" + +#: src/pinfo.c:486 +msgid "File not found. Press any key..." +msgstr "Không tìm thấy tập tin. Bấm phím nào..." + +#: src/pinfo.c:506 +#, c-format +msgid "Unexpected error.\n" +msgstr "Gặp lỗi bất ngờ.\n" + +#: src/pinfo.c:559 +msgid "Tag table not found. Trying to create alternate..." +msgstr "Không tìm thấy bảng thẻ. Đang cố tạo điều thay thế..." + +#: src/pinfo.c:645 +#, c-format +msgid "Security warning: Unable to get GID of group called: %s\n" +msgstr "Cảnh báo bảo mật: không thể lấy GID của nhóm tên: %s\n" + +#: src/pinfo.c:665 +#, c-format +msgid "Security warning: Unable to get UID of user called: %s\n" +msgstr "Cảnh báo bảo mật: không thể lấy UID của người dùng tên: %s\n" + +#: ../templates:5 +msgid "Which webserver would you like to configure automatically?" +msgstr "Bạn có muốn tự động cấu hình trình phục vụ Mạng nào?" + +#: ../templates:5 +msgid "" +"LDAP Account Manager supports any webserver that supports PHP4, but this " +"automatic configuration process only supports Apache and Apache2. If you " +"choose to configure Apache(2) LAM can be accessed at http(s)://localhost/lam" +msgstr "" +"Bộ Quản lý Tài khoản LDAP hỗ trợ trình phục vụ nào cũng hỗ trợ PHP4, nhưng " +"mà tiến trình tự động cấu hình này chỉ hỗ trợ Apache và Apache2 thôi. Nếu " +"bạn chọn cấu hình Apache(2), BQT có thể được truy cập tại ." + +#: ../templates:13 +msgid "Enter alias:" +msgstr "Gõ bí danh:" + +#: ../templates:13 +msgid "" +"LAM will add an alias to your httpd.conf which allows you to access LAM at " +"http(s)://localhost/lam. If you want an alias other than \"lam\" please " +"specify it here." +msgstr "" +"BQT sẽ thêm một bí danh vào tập tin cấu hình của bạn, mà cho " +"phép bạn truy cập BQT tại . Nếu bạn muốn có bí danh " +"khác với « lam », hãy gõ nó vào đây." + +#: ../templates:21 +msgid "Enter master configuration password (clear text):" +msgstr "Gõ mật khẩu cấu hình chủ (chữ xem được):" + +#: ../templates:21 +msgid "" +"The configuration profiles are secured by a master password. You will need " +"it to create and delete profiles. As default it is set to \"lam\" and can be " +"changed directly in LAM. But you can also change it now." +msgstr "" +"Những hồ sơ cấu hình đựơc bảo mật bởi một mật khẩu chủ. Bạn sẽ cần thiết nó " +"để tạo và xoá bỏ hồ sơ đó. Mặc định là « lam » và có thể được thay đổi trực " +"tiếp từ BQT. Bạn cũng có thể thay đổi nó ngay bây giờ." + +#: ../templates:30 +msgid "Would you like to restart your webserver(s) now?" +msgstr "Vậy bạn có muốn khởi chạy trình phục vụ Mạng không?" + +#: ../templates:30 +msgid "Your webserver(s) need to be restarted in order to apply the changes." +msgstr "" +"Cần phải khởi chạy lại trình phục vụ Mạng, để làm cho các thay đổi hoạt động." + +#: ../templates:35 +msgid "Upgrade from pre-0.5.0 versions" +msgstr "Nâng cấp từ phiên bản trước 0.5.0" + +#: ../templates:35 +msgid "" +"Please note that this version uses new file formats for configuration and " +"account profiles. You will have to update your configuration and create new " +"account profiles." +msgstr "" +"Hãy ghi chú rằng phiên bản này sử dụng khuôn dạng tập tin mới với cấu hình " +"và hồ sơ tài khoản. Bạn sẽ phải cập nhật cấu hình, và tạo hồ sơ tài khoản " +"mới." + +#: ../a11y/addressbook/ea-minicard-view.c:169 +msgid "evolution addressbook" +msgstr "Sổ địa chỉ Evolution" + +#: ../a11y/addressbook/ea-minicard-view.c:34 +#: ../addressbook/gui/component/addressbook-component.c:225 ../main.c:158 +msgid "New Contact" +msgstr "Liên lạc mới" + +#: ../a11y/addressbook/ea-minicard-view.c:35 +#: ../addressbook/gui/component/addressbook-component.c:233 +msgid "New Contact List" +msgstr "Danh sách liên lạc mới" + +#: ../a11y/addressbook/ea-minicard-view.c:152 +#, c-format +msgid "current addressbook folder has %d card" +msgid_plural "current addressbook folder has %d card" +msgstr[0] "thư mục sổ địa chỉ hiện thời có %d thẻ" + +#: ../src/menus.c:259 ../glade/glade_project_window.c:374 +#: ../widgets/gtk+.xml.in.h:136 +msgid "Open" +msgstr "Mở" + +#: ../a11y/addressbook/ea-minicard.c:141 +msgid "Contact List: " +msgstr "Danh sách liên lạc:" + +#: ../a11y/addressbook/ea-minicard.c:142 +msgid "Contact: " +msgstr "Liên lạc: " + +#: ../a11y/addressbook/ea-minicard.c:168 +msgid "evolution minicard" +msgstr "thẻ tí tị evolution" + +#: ../a11y/calendar/ea-cal-view-event.c:235 +msgid "It has alarms." +msgstr "Nó có bảo động." + +#: ../a11y/calendar/ea-cal-view-event.c:238 +msgid "It has recurrences." +msgstr "Nó có nhiều lần." + +#: ../a11y/calendar/ea-cal-view-event.c:241 +msgid "It is a meeting." +msgstr "Nó là cuộc họp." + +#: ../a11y/calendar/ea-cal-view-event.c:247 +#, c-format +msgid "Calendar Event: Summary is %s." +msgstr "Sự kiện lịch: tóm tắt là %s." + +#: ../a11y/calendar/ea-cal-view-event.c:249 +msgid "Calendar Event: It has no summary." +msgstr "Sự kiện lịch: chưa có tóm tắt." + +#: ../a11y/calendar/ea-cal-view-event.c:268 +msgid "calendar view event" +msgstr "sự kiện xem lịch" + +#: ../a11y/calendar/ea-cal-view-event.c:485 +msgid "Grab Focus" +msgstr "Được chú ý" + +#: ../a11y/calendar/ea-cal-view.c:306 +msgid "New Appointment" +msgstr "Cuộc hẹn mới" + +#: ../a11y/calendar/ea-cal-view.c:307 +msgid "New All Day Event" +msgstr "Sự kiện nguyên ngày mới" + +#: ../a11y/calendar/ea-cal-view.c:308 ../calendar/gui/e-calendar-view.c:1506 +msgid "New Meeting" +msgstr "Cuộc họp mới" + +#: ../a11y/calendar/ea-cal-view.c:309 +msgid "Go to Today" +msgstr "Đi tới ngày hôm nay" + +#: ../a11y/calendar/ea-cal-view.c:310 +msgid "Go to Date" +msgstr "Đi tới ngày" + +#: ../a11y/calendar/ea-day-view-main-item.c:299 +#: ../a11y/calendar/ea-week-view-main-item.c:301 +msgid "a table to view and select the current time range" +msgstr "một bảng cho phép xem và chọn phạm vị thời gian hiện có" + +#: ../a11y/calendar/ea-day-view.c:146 ../a11y/calendar/ea-week-view.c:148 +#, c-format +msgid "It has %d event." +msgid_plural "It has %d event." +msgstr[0] "Nó có %d sự kiện." + +#: ../a11y/calendar/ea-day-view.c:148 ../a11y/calendar/ea-week-view.c:150 +msgid "It has no events." +msgstr "Nó không có sự kiện nào." + +#: ../a11y/calendar/ea-day-view.c:152 +#, c-format +msgid "Work Week View: %s. %s" +msgstr "Khung xem tuần làm việc: %s. %s" + +#: ../a11y/calendar/ea-day-view.c:155 +#, c-format +msgid "Day View: %s. %s" +msgstr "Khung xem ngày: %s. %s" + +#: ../a11y/calendar/ea-day-view.c:186 +msgid "calendar view for a work week" +msgstr "khung xem lịch cho một tuần làm việc" + +#: ../a11y/calendar/ea-day-view.c:188 +msgid "calendar view for one or more days" +msgstr "khung xem lịch cho một hay nhiều ngày" + +#: ../calendar/gui/calendar-component.c:660 +msgid "%A %d %b %Y" +msgstr "%A %d %b %Y" + +#: ../calendar/gui/calendar-component.c:663 ../calendar/gui/e-day-view.c:1514 +msgid "%a %d %b" +msgstr "%a %d %b" + +#: ../calendar/gui/calendar-component.c:672 +msgid "%a %d %b %Y" +msgstr "%a %A, ngày %e, %B, năm %Y" + +#: ../calendar/gui/calendar-component.c:699 +msgid "%d %b %Y" +msgstr "%d %b %Y" + +#: ../calendar/gui/calendar-component.c:689 ../calendar/gui/e-day-view.c:1530 +msgid "%d %b" +msgstr "%d %b" + +#: ../calendar/importers/icalendar-importer.c:738 +msgid "Gnome Calendar" +msgstr "Lịch Gnome" + +#: ../a11y/calendar/ea-gnome-calendar.c:290 +msgid "search bar" +msgstr "thanh tìm" + +#: ../a11y/calendar/ea-gnome-calendar.c:291 +msgid "evolution calendar search bar" +msgstr "thanh tìm kiếm lịch Evolution" + +#: ../a11y/calendar/ea-jump-button.c:149 +msgid "Jump button" +msgstr "Nút nhảy" + +#: ../a11y/calendar/ea-jump-button.c:158 +msgid "Click here, you can find more events." +msgstr "Nhấn vào đây để tìm sự kiện thêm nữa" + +#: ../a11y/calendar/ea-week-view.c:155 +#, c-format +msgid "Month View: %s. %s" +msgstr "Khung xem tháng: %s. %s" + +#: ../a11y/calendar/ea-week-view.c:159 +#, c-format +msgid "Week View: %s. %s" +msgstr "Khung xem tuần: %s. %s" + +#: ../a11y/calendar/ea-week-view.c:190 +msgid "calendar view for a month" +msgstr "khung xem lịch cho một tháng" + +#: ../a11y/calendar/ea-week-view.c:192 +msgid "calendar view for one or more weeks" +msgstr "khung xem lịch cho một hay nhiều tuần" + +#: ../a11y/e-table/gal-a11y-e-cell-popup.c:124 +msgid "popup" +msgstr "bật lên" + +#. action name +#: ../a11y/e-table/gal-a11y-e-cell-popup.c:125 +msgid "popup a child" +msgstr "bật lên một điều con" + +#: ../a11y/e-table/gal-a11y-e-cell-text.c:612 +msgid "edit" +msgstr "đổi" + +#: ../a11y/e-table/gal-a11y-e-cell-text.c:613 +msgid "begin editing this cell" +msgstr "bắt đầu sửa đổi ô này" + +#: ../a11y/e-table/gal-a11y-e-cell-toggle.c:151 +msgid "toggle" +msgstr "bật tắt" + +#. action name +#: ../a11y/e-table/gal-a11y-e-cell-toggle.c:152 +msgid "toggle the cell" +msgstr "bật/tắt ô này" + +#: ../a11y/e-table/gal-a11y-e-cell-tree.c:171 +msgid "expand" +msgstr "mở rộng" + +#: ../a11y/e-table/gal-a11y-e-cell-tree.c:172 +msgid "expands the row in the ETree containing this cell" +msgstr "mở rộng hàng trong ETree chứa ô này" + +#: ../a11y/e-table/gal-a11y-e-cell-tree.c:177 +msgid "collapse" +msgstr "co lại" + +#: ../a11y/e-table/gal-a11y-e-cell-tree.c:178 +msgid "collapses the row in the ETree containing this cell" +msgstr "co lại hàng trong ETree chứa ô này" + +#: ../a11y/e-table/gal-a11y-e-cell.c:107 +msgid "Table Cell" +msgstr "Ô bảng" + +#: ../widgets/table/e-table-click-to-add.c:575 +msgid "click to add" +msgstr "nhấn chuột để thêm" + +#: ../a11y/e-table/gal-a11y-e-table-click-to-add.c:53 +msgid "click" +msgstr "nhắp" + +#: ../a11y/e-table/gal-a11y-e-table-column-header.c:135 +msgid "sort" +msgstr "sắp xếp" + +#: ../a11y/widgets/ea-calendar-item.c:296 +#: ../a11y/widgets/ea-calendar-item.c:302 prefs.c:392 +msgid "%d %B %Y" +msgstr "%d %B %Y" + +#: ../a11y/widgets/ea-calendar-item.c:304 +#, c-format +msgid "Calendar: from %s to %s" +msgstr "Lịch: từ %s đến %s" + +#: ../a11y/widgets/ea-calendar-item.c:339 +msgid "evolution calendar item" +msgstr "mục lịch Evolution" + +#: ../a11y/widgets/ea-combo-button.c:40 +msgid "Combo Button" +msgstr "Nút tổ hợp" + +#: ../a11y/widgets/ea-combo-button.c:50 +msgid "Activate Default" +msgstr "Dùng mặc định" + +#: ../a11y/widgets/ea-combo-button.c:52 ../glade/gbwidgets/gbmenu.c:198 +#: ../widgets/gtk+.xml.in.h:143 +msgid "Popup Menu" +msgstr "Trình đơn bật lên" + +#: ../addressbook/addressbook.error.xml.h:1 +msgid "" +"A contact already exists with this address. Would you like to add a new card " +"with the same address anyway?" +msgstr "" +"Một liên lạc với địa chỉ này đã có. Bạn vẫn có muốn thêm một thẻ mới với " +"cùng địa chỉ không?" + +#: ../addressbook/addressbook.error.xml.h:2 +msgid "Address '{0}' already exists." +msgstr "Địa chỉ « {0} » đã có." + +#: ../addressbook/addressbook.error.xml.h:3 +msgid "Cannot move contact." +msgstr "Không di chuyển được liên lạc." + +#: ../addressbook/addressbook.error.xml.h:4 +msgid "Category editor not available." +msgstr "Không có bộ biên soạn phân loại." + +#: ../addressbook/addressbook.error.xml.h:5 +msgid "" +"Check to make sure your password is spelled correctly and that you are using " +"a supported login method. Remember that many passwords are case sensitive; " +"your caps lock might be on." +msgstr "" +"Hãy kiểm tra xem mật khẩu của bạn được gõ chính xác và bạn sử dụng phương " +"thức đăng nhập được hỗ trợ. Lưu ý rằng nhiều mật khẩu phân biệt chữ hoa, chữ " +"thường; và hãy chắc là phím Caps Lock của bạn được tắt." + +#: ../addressbook/addressbook.error.xml.h:6 +msgid "Could not get schema information for LDAP server." +msgstr "Không thể lấy thông tin giản đồ cho máy phục vụ LDAP." + +#: ../addressbook/addressbook.error.xml.h:7 +msgid "Could not remove addressbook." +msgstr "Không thể gỡ bỏ sổ địa chỉ." + +#: ../addressbook/addressbook.error.xml.h:8 +msgid "" +"Currently you can access only GroupWise System Address Book from Evolution. " +"Please use some other GroupWise mail client once, to get your GroupWise " +"Frequent Contacts and Groupwise Personal Contacts folders." +msgstr "" +"Hiện thời bạn có thể truy cập chỉ Sổ Địa Chỉ hệ thống Groupwise từ " +"Evolution. Hãy chạy một lần ứng dụng khách thư Groupwise khác, để lấy các " +"thư mục GroupWise Frequent Contacts (liên lạc thường) và GroupWise Personal " +"Contacts (liên lạc cá nhân)." + +#: ../addressbook/addressbook.error.xml.h:9 +#: ../addressbook/addressbook.error.xml.h:8 +msgid "Delete address book '{0}'?" +msgstr "Xoá bỏ sổ địa chỉ « {0} » không?" + +#: ../addressbook/addressbook.error.xml.h:10 +#: ../addressbook/addressbook.error.xml.h:9 +msgid "Error loading addressbook." +msgstr "Gặp lỗi khi tải sổ địa chỉ." + +#: ../addressbook/addressbook.error.xml.h:11 +#: ../addressbook/addressbook.error.xml.h:10 +msgid "Error saving {0} to {1}: {2}" +msgstr "Gặp lỗi khi lưu {0} vào {1}: {2}" + +#: ../addressbook/addressbook.error.xml.h:12 +#: ../addressbook/addressbook.error.xml.h:11 +msgid "Failed to authenticate with LDAP server." +msgstr "Lỗi xác thực với máy phục vụ LDAP." + +#: ../addressbook/addressbook.error.xml.h:13 +msgid "GroupWise Address book creation:" +msgstr "Tạo Sổ địa chỉ GroupWise:" + +#: ../addressbook/addressbook.error.xml.h:14 +#: ../addressbook/addressbook.error.xml.h:12 +msgid "LDAP server did not respond with valid schema information." +msgstr "Máy phục vụ LDAP không trả lời với thông tin giản đồ hợp lệ." + +#: ../addressbook/addressbook.error.xml.h:15 +#: ../addressbook/addressbook.error.xml.h:13 +msgid "Server Version" +msgstr "Phiên bản máy phục vụ" + +#: ../addressbook/addressbook.error.xml.h:16 +#: ../addressbook/addressbook.error.xml.h:14 +#: ../calendar/calendar.error.xml.h:44 +msgid "Some features may not work properly with your current server" +msgstr "" +"Có lẽ một số tính năng sẽ không hoạt động với máy phục vụ hiện thời của bạn." + +#: ../addressbook/addressbook.error.xml.h:17 +#: ../addressbook/addressbook.error.xml.h:15 +msgid "The Evolution addressbook has quit unexpectedly." +msgstr "Sổ địa chỉ Evolution đã thoát bất ngờ." + +#: ../addressbook/addressbook.error.xml.h:18 +#: ../addressbook/addressbook.error.xml.h:16 +msgid "" +"The image you have selected is large. Do you want to resize and store it?" +msgstr "Có ảnh lớn. Bạn có muốn thay đổi kích thước nó, và cất giữ nó không?" + +#: ../addressbook/addressbook.error.xml.h:19 +#: ../addressbook/addressbook.error.xml.h:17 +msgid "" +"This LDAP server may use an older version of LDAP, which does not support " +"this functionality or it may be misconfigured. Ask your administrator for " +"supported search bases." +msgstr "" +"Máy phục vụ LDAP này có lẽ dùng phiên bản LDAP cũ, không hỗ trợ tính năng " +"này hoặc bị cấu hình sai. Hãy hỏi quản trị hệ thống về những cơ sở tìm kiếm " +"được hỗ trợ." + +#: ../addressbook/addressbook.error.xml.h:20 +#: ../addressbook/addressbook.error.xml.h:18 +msgid "This address book will be removed permanently." +msgstr "Sẽ xoá bỏ sổ địa chỉ này hoàn toàn." + +#: ../addressbook/addressbook.error.xml.h:21 +#: ../addressbook/addressbook.error.xml.h:19 +msgid "This addressbook could not be opened." +msgstr "Không thể mở sổ địa chỉ này." + +#: ../addressbook/addressbook.error.xml.h:22 +#: ../addressbook/addressbook.error.xml.h:20 +msgid "This addressbook server does not have any suggested search bases." +msgstr "Máy phục vụ sổ địa chỉ này không đề nghị cơ sở tìm kiếm nào." + +#: ../addressbook/addressbook.error.xml.h:23 +#: ../addressbook/addressbook.error.xml.h:21 +msgid "" +"This addressbook server might be unreachable or the server name may be " +"misspelled or your network connection could be down." +msgstr "" +"Không thể tiếp cận máy phục vụ sổ địa chỉ này, hoặc tên máy phục vụ đã gõ " +"sai, hoặc bị ngắt kết nối." + +#: ../addressbook/addressbook.error.xml.h:24 +#: ../addressbook/addressbook.error.xml.h:22 +msgid "This server does not support LDAPv3 schema information." +msgstr "Máy phục vụ này không hỗ trợ thông tin giản đồ LDAPv3." + +#: ../addressbook/addressbook.error.xml.h:25 +#: ../addressbook/addressbook.error.xml.h:23 +msgid "Unable to open addressbook" +msgstr "Không thể mở sổ địa chỉ." + +#: ../addressbook/addressbook.error.xml.h:26 +#: ../addressbook/addressbook.error.xml.h:24 +msgid "Unable to perform search." +msgstr "Không thực hiện được tìm kiếm." + +#: ../addressbook/addressbook.error.xml.h:27 +#: ../addressbook/addressbook.error.xml.h:25 +msgid "Unable to save {0}." +msgstr "Không thể lưu {0}." + +#: ../addressbook/addressbook.error.xml.h:28 +#: ../addressbook/addressbook.error.xml.h:26 +msgid "Would you like to save your changes?" +msgstr "Bạn có muốn lưu các thay đổi chứ?" + +#: ../addressbook/addressbook.error.xml.h:29 +#: ../addressbook/addressbook.error.xml.h:27 +msgid "" +"You are attempting to move a contact from one addressbook to another but it " +"cannot be removed from the source. Do you want to save a copy instead?" +msgstr "" +"Bạn đang cố di chuyển liên lạc từ sổ địa chỉ này sang sổ địa chỉ khác nhưng " +"mà không thể gỡ bỏ nó khỏi nguồn. Như thế thì bạn có muốn tạo một bản sao " +"thay vào đó không?" + +#: ../addressbook/addressbook.error.xml.h:30 +#: ../addressbook/addressbook.error.xml.h:28 +#: ../calendar/calendar.error.xml.h:59 +msgid "" +"You are connecting to an unsupported GroupWise server and may encounter " +"problems using Evolution. For best results the server should be upgraded to " +"a supported version" +msgstr "" +"Bạn đang kết nối đến một máy phục vụ Groupwise không được hỗ trợ thì có lẽ " +"sẽ gặp khó khăn sử dụng trình Evolution. Để được kết quả tốt nhất, bạn nên " +"nâng cấp máy phục vụ lên một phiên bản được hỗ trợ." + +#: ../addressbook/addressbook.error.xml.h:31 +#: ../addressbook/addressbook.error.xml.h:29 +msgid "" +"You have made modifications to this contact. Do you want to save these " +"changes?" +msgstr "Bạn đã chỉnh sửa liên lạc này, thì có muốn lưu các thay đổi lại chứ?" + +#: ../addressbook/addressbook.error.xml.h:32 +#: ../addressbook/addressbook.error.xml.h:30 +msgid "" +"Your contacts for {0} will not be available until Evolution is restarted." +msgstr "" +"Các liên lạc của bạn cho {0} không thể sử dụng cho tới khi khởi chạy lại " +"Evolution." + +#: ../addressbook/addressbook.error.xml.h:34 +#: ../addressbook/addressbook.error.xml.h:32 +msgid "_Discard" +msgstr "_Hủy bỏ" + +#: ../addressbook/addressbook.error.xml.h:35 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:76 +#: ../addressbook/addressbook.error.xml.h:33 +msgid "{0}" +msgstr "{0}" + +#: ../addressbook/addressbook.error.xml.h:36 +#: ../addressbook/addressbook.error.xml.h:34 +msgid "{1}" +msgstr "{1}" + +#: ../addressbook/conduit/address-conduit.c:300 +#: ../addressbook/conduit/address-conduit.c:298 +msgid "Default Sync Address:" +msgstr "Địa chỉ đồng bộ mặc định:" + +#: ../addressbook/conduit/address-conduit.c:1184 +msgid "Could not load addressbook" +msgstr "Không thể tải sổ địa chỉ." + +#: ../addressbook/conduit/address-conduit.c:1255 +msgid "Could not read pilot's Address application block" +msgstr "Không thể đọc khối ứng dụng Địa chỉ của pilot" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:1 +msgid "Autocompletion" +msgstr "Tự động hoàn tất" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:2 +msgid "C_ontacts" +msgstr "_Liên lạc" + +#: ../extensions/certificates/certificates.ephy-extension.in.in.h:1 +msgid "Certificates" +msgstr "Chứng nhận" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:4 +msgid "Configure autocomplete here" +msgstr "Cấu hình tự động hoàn tất ở đây" + +#: ../storage/exchange-hierarchy-foreign.c:251 +msgid "Contacts" +msgstr "Liên lạc" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:6 +msgid "Evolution Addressbook" +msgstr "Sổ địa chỉ Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:7 +msgid "Evolution Addressbook address pop-up" +msgstr "Bật lên địa chỉ của Sổ địa chỉ Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:8 +msgid "Evolution Addressbook address viewer" +msgstr "Khung xem địa chỉ của Sổ địa chỉ Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:9 +msgid "Evolution Addressbook card viewer" +msgstr "Khung xem thẻ của Sổ địa chỉ Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:10 +msgid "Evolution Addressbook component" +msgstr "Thành phần Sổ địa chỉ Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:11 +msgid "Evolution S/Mime Certificate Management Control" +msgstr "Điều khiển Quản lý Chứng nhận S/MIME Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:12 +msgid "Evolution folder settings configuration control" +msgstr "Điều khiển cấu hình thiết lập thư mục Evolution" + +#: ../addressbook/gui/component/GNOME_Evolution_Addressbook.server.in.in.h:13 +msgid "Manage your S/MIME certificates here" +msgstr "Quản lý chứng nhận S/MIME của bạn ở đây" + +#: ../Util/Evolution.cs:45 ../Util/Evolution.cs:163 +msgid "On This Computer" +msgstr "Trên máy này" + +#: ../48x48/emblems/emblem-personal.icon.in.h:1 ../data/browser.xml.h:66 +#: ../storage/sunone-folder-tree.c:297 +msgid "Personal" +msgstr "Cá nhân" + +#: ../addressbook/gui/component/addressbook-migrate.c:520 +msgid "On LDAP Servers" +msgstr "Trên máy phục vụ LDAP" + +#: ../addressbook/gui/component/addressbook-component.c:226 +msgid "_Contact" +msgstr "_Liên lạc" + +#: ../addressbook/gui/component/addressbook-component.c:227 +msgid "Create a new contact" +msgstr "Tạo liên lạc mới" + +#: ../addressbook/gui/component/addressbook-component.c:234 +msgid "Contact _List" +msgstr "_Danh sách liên lạc" + +#: ../addressbook/gui/component/addressbook-component.c:235 +msgid "Create a new contact list" +msgstr "Tạo danh sách liên lạc mới" + +#: ../addressbook/gui/component/addressbook-view.c:763 +msgid "New Address Book" +msgstr "Sổ địa chỉ mới" + +#: ../addressbook/gui/component/addressbook-component.c:242 +msgid "Address _Book" +msgstr "_Sổ địa chỉ" + +#: ../addressbook/gui/component/addressbook-component.c:243 +msgid "Create a new address book" +msgstr "Tạo sổ địa chỉ mới" + +#: ../addressbook/gui/component/addressbook-component.c:385 +msgid "Failed upgrading Addressbook settings or folders." +msgstr "Lỗi nâng cấp thiết lập Sổ địa chỉ hoặc thư mục." + +#: ../addressbook/gui/component/addressbook-config.c:329 +msgid "Base" +msgstr "Cơ sở" + +#: ../data/glade/resource-dialog.glade.h:14 ../src/drivel.glade.h:83 +msgid "_Type:" +msgstr "_Kiểu :" + +#: ../addressbook/gui/component/addressbook-config.c:634 +#: ../addressbook/gui/component/addressbook-config.c:607 +msgid "Copy book content locally for offline operation" +msgstr "Sao chép nội dung sổ về máy để phục vụ các thao tác ngoại tuyến" + +#: ../mail/importers/pine-importer.c:393 +msgid "Addressbook" +msgstr "Sổ địa chỉ" + +#: ../addressbook/gui/component/addressbook-config.c:986 src/common/text.c:642 +#: ../addressbook/gui/component/addressbook-config.c:907 src/common/text.c:646 +msgid "Server Information" +msgstr "Thông tin máy phục vụ" + +#: ../data/SoftwarePropertiesDialogs.glade.h:20 +msgid "Authentication" +msgstr "Xác thực" + +#: ../glom/mode_find/notebook_find.cc:28 +msgid "Details" +msgstr "Chi tiết" + +#: ../addressbook/gui/component/addressbook-config.c:992 +#: ../addressbook/gui/component/addressbook-config.c:913 +msgid "Searching" +msgstr "Đang tìm" + +#: ../addressbook/gui/component/addressbook-config.c:994 +msgid "Downloading" +msgstr "Đang tải về" + +#: ../addressbook/gui/component/ldap-config.glade.h:14 +msgid "Address Book Properties" +msgstr "Thuộc tính Sổ địa chỉ" + +#: ../calendar/gui/migration.c:142 ../mail/em-migrate.c:1190 +msgid "Migrating..." +msgstr "Đang nâng cấp..." + +#: ../storage/exchange-migrate.c:129 +#, c-format +msgid "Migrating `%s':" +msgstr "Đang nâng cấp « %s »" + +#: ../addressbook/gui/component/addressbook-migrate.c:653 +#: ../addressbook/gui/component/addressbook-migrate.c:648 +msgid "LDAP Servers" +msgstr "Máy phục vụ LDAP" + +#: ../addressbook/gui/component/addressbook-migrate.c:768 +#: ../addressbook/gui/component/addressbook-migrate.c:763 +msgid "Autocompletion Settings" +msgstr "Thiết lập Tự động hoàn tất" + +#: ../addressbook/gui/component/addressbook-migrate.c:1143 +#: ../addressbook/gui/component/addressbook-migrate.c:1134 +msgid "" +"The location and hierarchy of the Evolution contact folders has changed " +"since Evolution 1.x.\n" +"\n" +"Please be patient while Evolution migrates your folders..." +msgstr "" +"Địa chỉ và cây thư mục liên lạc Evolution đã thay đổi so với Evolution 1.x.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi các thư mục..." + +#: ../addressbook/gui/component/addressbook-migrate.c:1157 +#: ../addressbook/gui/component/addressbook-migrate.c:1148 +msgid "" +"The format of mailing list contacts has changed.\n" +"\n" +"Please be patient while Evolution migrates your folders..." +msgstr "" +"Định dạng của liên lạc hộp thư chung đã thay đổi.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi các thư mục của bạn..." + +#: ../addressbook/gui/component/addressbook-migrate.c:1166 +#: ../addressbook/gui/component/addressbook-migrate.c:1157 +msgid "" +"The way Evolution stores some phone numbers has changed.\n" +"\n" +"Please be patient while Evolution migrates your folders..." +msgstr "" +"Cách Evolution lưu một phần số điện thoại đã thay đổi.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi các thư mục của bạn..." + +#: ../addressbook/gui/component/addressbook-migrate.c:1176 +#: ../addressbook/gui/component/addressbook-migrate.c:1167 +msgid "" +"Evolution's Palm Sync changelog and map files have changed.\n" +"\n" +"Please be patient while Evolution migrates your Pilot Sync data..." +msgstr "" +"Các tập tin bản ghi thay đổi và bản đồ đều của Evolution Palm Sync (trình " +"đồng bộ hóa máy tính cầm tay chạy hệ thống Palm) đã thay đổi.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi dữ liệu Pilot Sync..." + +#: ../addressbook/gui/component/addressbook-view.c:769 +msgid "_New Address Book" +msgstr "Sổ địa chỉ _mới" + +#: ../addressbook/gui/component/addressbook-view.c:1197 +#: ../addressbook/gui/component/addressbook-view.c:1144 +msgid "Contact Source Selector" +msgstr "Bộ chọn nguồn liên lạc" + +#: ../addressbook/gui/component/addressbook.c:99 +#: ../libedataserverui/e-book-auth-util.c:89 +msgid "Accessing LDAP Server anonymously" +msgstr "Truy cập vô danh tới máy phục vụ LDAP" + +#: ../libedataserverui/e-book-auth-util.c:185 +msgid "Failed to authenticate.\n" +msgstr "Lỗi xác thực.\n" + +#: ../calendar/libecal/e-cal.c:1650 ../libedataserverui/e-book-auth-util.c:192 +#, c-format +msgid "Enter password for %s (user %s)" +msgstr "Hãy gõ mật khẩu cho %s (người dùng %s)" + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:1 +msgid "Autocomplete length" +msgstr "Độ dài tự động hoàn tất" + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:2 +msgid "EFolderList XML for the list of completion URIs" +msgstr "" +"XML EFolderList (danh sách thư mục điện) cho danh sách các địa chỉ Mạng cần " +"gõ xong" + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:3 +msgid "EFolderList XML for the list of completion URIs." +msgstr "" +"XML EFolderList (danh sách thư mục điện) cho danh sách các địa chỉ Mạng cần " +"gõ xong." + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:4 +msgid "" +"Position of the vertical pane, between the card and list views and the " +"preview pane, in pixels." +msgstr "" +"Vị trí của ô cửa sổ dọc giữa khung xem thẻ và khung xem danh sách và ô cửa " +"sổ xem trước, theo điểm ảnh." + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:6 +msgid "" +"The number of characters that must be typed before Evolution will attempt to " +"autocomplete." +msgstr "Số ký tự cần gõ trước khi trình Evolution sẽ cố tự động hoàn tất." + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:7 +msgid "URI for the folder last used in the select names dialog" +msgstr "Địa chỉ Mạng cho thư mục đã dùng cuối cùng trong hộp thoại chọn tên." + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:8 +msgid "URI for the folder last used in the select names dialog." +msgstr "Địa chỉ Mạng cho thư mục đã dùng cuối cùng trong hộp thoại chọn tên." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:55 +msgid "Vertical pane position" +msgstr "Vị trí ô cửa sổ dọc" + +#: ../addressbook/gui/component/apps_evolution_addressbook.schemas.in.in.h:10 +msgid "Whether to show the preview pane." +msgstr "Có nên hiển thị ô cửa sổ xem trước hay không." + +#: ../gnopi/cmdmapui.c:182 +msgid "1" +msgstr "1" + +#: ../addressbook/gui/component/ldap-config.glade.h:2 +#: ../addressbook/gui/component/ldap-config.glade.h:3 +msgid "3268" +msgstr "3268" + +#: ../addressbook/gui/component/ldap-config.glade.h:3 +#: ../addressbook/gui/component/ldap-config.glade.h:4 +msgid "389" +msgstr "389" + +#: ../gnopi/cmdmapui.c:186 +msgid "5" +msgstr "5" + +#: ../addressbook/gui/component/ldap-config.glade.h:5 +#: ../addressbook/gui/component/ldap-config.glade.h:6 +msgid "636" +msgstr "636" + +#: ../addressbook/gui/component/ldap-config.glade.h:7 +msgid "Authentication" +msgstr "Xác thực" + +#: ../addressbook/gui/component/ldap-config.glade.h:7 +msgid "Display" +msgstr "Hiển thị" + +#: ../addressbook/gui/component/ldap-config.glade.h:8 +#: ../addressbook/gui/component/ldap-config.glade.h:9 +msgid "Downloading" +msgstr "Tải về" + +#: ../addressbook/gui/component/ldap-config.glade.h:9 +#: ../addressbook/gui/component/ldap-config.glade.h:10 +msgid "Searching" +msgstr "Tìm kiếm" + +#: ../addressbook/gui/component/ldap-config.glade.h:10 +#: ../addressbook/gui/component/ldap-config.glade.h:11 +msgid "Server Information" +msgstr "Thông tin máy phục vụ" + +#: ../addressbook/gui/component/ldap-config.glade.h:11 +msgid "Type:" +msgstr "Loại:" + +#: ../addressbook/gui/component/ldap-config.glade.h:15 +#: ../addressbook/gui/component/ldap-config.glade.h:16 +msgid "Anonymously" +msgstr "Vô danh" + +#: ../data/glade/song-info.glade.h:3 ../src/gnome-schedule.glade.h:16 +msgid "Basic" +msgstr "Cơ bản" + +#: ../addressbook/gui/component/ldap-config.glade.h:18 +#: ../addressbook/gui/component/ldap-config.glade.h:19 +msgid "Distinguished name" +msgstr "Tên phân biệt" + +#: ../addressbook/gui/component/ldap-config.glade.h:19 +#: ../addressbook/gui/component/ldap-config.glade.h:20 +msgid "Email address" +msgstr "Địa chỉ thư" + +#: ../addressbook/gui/component/ldap-config.glade.h:20 +#: ../addressbook/gui/component/ldap-config.glade.h:21 +msgid "" +"Evolution will use this email address to authenticate you with the server." +msgstr "" +"Evolution sẽ dùng địa chỉ thư điện tử này để xác thực bạn với máy phục vụ." + +#: ../addressbook/gui/component/ldap-config.glade.h:21 +#: ../addressbook/gui/component/ldap-config.glade.h:22 +msgid "Find Possible Search Bases" +msgstr "Tìm mọi cơ sở tìm có thể" + +#: ../addressbook/gui/component/ldap-config.glade.h:23 +#: ../addressbook/gui/component/ldap-config.glade.h:24 +msgid "Lo_gin:" +msgstr "Đăng _nhập:" + +#: ../addressbook/gui/component/ldap-config.glade.h:25 +#: ../addressbook/gui/component/ldap-config.glade.h:26 +msgid "One" +msgstr "Một" + +#: ../addressbook/gui/component/ldap-config.glade.h:26 +msgid "Search Filter" +msgstr "Bộ lọc tìm kiếm" + +#: ../addressbook/gui/component/ldap-config.glade.h:27 +msgid "Search _base:" +msgstr "_Cơ sở tìm:" + +#: ../addressbook/gui/component/ldap-config.glade.h:28 +msgid "Search _filter:" +msgstr "Bộ _lọc tìm kiếm" + +#: ../addressbook/gui/component/ldap-config.glade.h:29 +msgid "Search filter" +msgstr "Bộ lọc tìm kiếm" + +#: ../addressbook/gui/component/ldap-config.glade.h:30 +msgid "" +"Search filter is the type of the objects searched for, while performing the " +"search. If this is not modified, by default search will be performed on " +"objectclass of the type \"person\"." +msgstr "" +"Bộ lọc tìm kiếm là kiểu đối tượng cần tìm kiếm. Nếu nó chưa được sửa đổi, " +"mặc định là hạng đối tượng kiểu « person » (người) sẽ được tìm kiếm." + +#: ../addressbook/gui/component/ldap-config.glade.h:31 +#: ../addressbook/gui/component/ldap-config.glade.h:28 +msgid "" +"Selecting this option means that Evolution will only connect to your LDAP " +"server if your LDAP server supports SSL or TLS." +msgstr "" +"Chọn tùy chọn này nghĩa là Evolution sẽ kết nối tới máy phục vụ LDAP của bạn " +"chỉ nếu máy phục vụ LDAP đó hỗ trợ SSL hoặc TLS." + +#: ../addressbook/gui/component/ldap-config.glade.h:32 +#: ../addressbook/gui/component/ldap-config.glade.h:29 +msgid "" +"Selecting this option means that Evolution will only try to use SSL/TLS if " +"you are in a insecure environment. For example, if you and your LDAP server " +"are behind a firewall at work, then Evolution doesn't need to use SSL/TLS " +"because your connection is already secure." +msgstr "" +"Chọn tùy chọn này nghĩa là Evolution sẽ cố dùng SSL/TLS chỉ nếu bạn trong " +"môi trường bất an. Ví dụ, nếu bạn và máy phục vụ LDAP của bạn nằm sau tường " +"lửa tại chỗ làm, thì Evolution sẽ không cần dùng SSL/TLS vì kết nối đã đủ an " +"toàn." + +#: ../addressbook/gui/component/ldap-config.glade.h:33 +#: ../addressbook/gui/component/ldap-config.glade.h:30 +msgid "" +"Selecting this option means that your server does not support either SSL or " +"TLS. This means that your connection will be insecure, and that you will be " +"vulnerable to security exploits. " +msgstr "" +"Chọn tùy chọn này nghĩa là máy phục vụ của bạn không hỗ trợ cả SSL lẫn TLS. " +"Điều này nghĩa là kết nối của bạn không an toàn, có thể bị lỗ hổng bảo mật." + +#: ../addressbook/gui/component/ldap-config.glade.h:34 +#: ../addressbook/gui/component/ldap-config.glade.h:31 +msgid "Sub" +msgstr "Con" + +#: ../addressbook/gui/component/ldap-config.glade.h:35 +#: ../addressbook/gui/component/ldap-config.glade.h:32 +msgid "Supported Search Bases" +msgstr "Cơ sở tìm được hỗ trợ" + +#: ../addressbook/gui/component/ldap-config.glade.h:36 +#: ../addressbook/gui/component/ldap-config.glade.h:33 +msgid "" +"The search base is the distinguished name (DN) of the entry where your " +"searches will begin. If you leave this blank, the search will begin at the " +"root of the directory tree." +msgstr "" +"Cơ sở tìm là tên phân biệt (TP) của mục, chỗ bắt đầu tìm kiếm. Nếu bạn bỏ " +"trống chỗ này, tìm kiếm sẽ được bắt đầu từ gốc cây thư mục." + +#: ../addressbook/gui/component/ldap-config.glade.h:37 +#: ../addressbook/gui/component/ldap-config.glade.h:34 +msgid "" +"The search scope defines how deep you would like the search to extend down " +"the directory tree. A search scope of \"sub\" will include all entries below " +"your search base. A search scope of \"one\" will only include the entries " +"one level beneath your base." +msgstr "" +"Phạm vi tìm kiếm cho biết độ sâu tìm kiếm đi xuống trong cây thư mục. Phạm " +"vi tìm kiếm « con » sẽ bao gồm mọi mục dưới cơ sở tìm. Phạm vi tìm kiếm « " +"một » sẽ chỉ tìm những mục nằm một mức độ dưới trong cơ sở tìm thôi." + +#: ../addressbook/gui/component/ldap-config.glade.h:38 +#: ../addressbook/gui/component/ldap-config.glade.h:35 +msgid "" +"This is the full name of your ldap server. For example, \"ldap.mycompany.com" +"\"." +msgstr "" +"Đây là tên đầy đủ của máy phục vụ LDAP. Ví dụ :\n" +"ldap.côngtytôi.com.vn" + +#: ../addressbook/gui/component/ldap-config.glade.h:39 +#: ../addressbook/gui/component/ldap-config.glade.h:36 +msgid "" +"This is the maximum number of entries to download. Setting this number to be " +"too large will slow down your address book." +msgstr "Đây là số mục tải về tối đa. Dùng số quá lớn sẽ làm chậm sổ địa chỉ." + +#: ../addressbook/gui/component/ldap-config.glade.h:40 +#: ../addressbook/gui/component/ldap-config.glade.h:37 +msgid "" +"This is the method Evolution will use to authenticate you. Note that " +"setting this to \"Email Address\" requires anonymous access to your ldap " +"server." +msgstr "" +"Đây là cách Evolution dùng để xác thực bạn. Chú ý rằng đặt cái này là\n" +"« Địa chỉ thư » yêu cầu truy cập vô danh tới máy phục vụ LDAP." + +#: ../addressbook/gui/component/ldap-config.glade.h:41 +#: ../addressbook/gui/component/ldap-config.glade.h:38 +msgid "" +"This is the name for this server that will appear in your Evolution folder " +"list. It is for display purposes only. " +msgstr "" +"Đây là tên máy phục vụ xuất hiện trong danh sách thư mục Evolution. Chỉ được " +"dùng với mục đích hiển thị thôi." + +#: ../addressbook/gui/component/ldap-config.glade.h:42 +#: ../addressbook/gui/component/ldap-config.glade.h:39 +msgid "" +"This is the port on the LDAP server that Evolution will try to connect to. A " +"list of standard ports has been provided. Ask your system administrator what " +"port you should specify." +msgstr "" +"Đây là số hiệu cổng của máy phục vụ LDAP mà Evolution sẽ cố kết nối đến nó. " +"Một danh sách các cổng chuẩn đã được cung cấp. Hãy hỏi quản trị hệ thống của " +"bạn để biết dùng cổng nào." + +#: ../addressbook/gui/component/ldap-config.glade.h:43 +#: ../addressbook/gui/component/ldap-config.glade.h:40 +msgid "Using distinguished name (DN)" +msgstr "Dùng tên phân biệt (TP)" + +#: ../addressbook/gui/component/ldap-config.glade.h:44 +#: ../addressbook/gui/component/ldap-config.glade.h:41 +msgid "Using email address" +msgstr "Dùng địa chỉ thư" + +#: ../mail/em-account-editor.c:301 +msgid "Whenever Possible" +msgstr "Bất cứ khi nào có thể" + +#: ../addressbook/gui/component/ldap-config.glade.h:46 +#: ../addressbook/gui/component/ldap-config.glade.h:43 +msgid "_Add Address Book" +msgstr "_Thêm Sổ địa chỉ" + +#: ../addressbook/gui/component/ldap-config.glade.h:47 +#: ../addressbook/gui/component/ldap-config.glade.h:44 +msgid "_Download limit:" +msgstr "_Ngưỡng tải về:" + +#: ../addressbook/gui/component/ldap-config.glade.h:48 +#: ../addressbook/gui/component/ldap-config.glade.h:45 +msgid "_Find Possible Search Bases" +msgstr "_Tìm mọi cơ sở tìm có thể" + +#: ../addressbook/gui/component/ldap-config.glade.h:49 +#: ../addressbook/gui/component/ldap-config.glade.h:46 +msgid "_Login method:" +msgstr "Cách đăng _nhập:" + +#: ../src/baobab-remote-connect-dialog.c:500 ../ui/muds.glade.h:53 +msgid "_Port:" +msgstr "_Cổng:" + +#: ../addressbook/gui/component/ldap-config.glade.h:52 +#: ../addressbook/gui/component/ldap-config.glade.h:49 +msgid "_Search scope:" +msgstr "_Phạm vi tìm:" + +#: ../capplets/mouse/gnome-mouse-properties.glade.h:26 +msgid "_Timeout:" +msgstr "_Thời hạn:" + +#: ../addressbook/gui/component/ldap-config.glade.h:55 +#: ../addressbook/gui/component/ldap-config.glade.h:52 +msgid "_Use secure connection:" +msgstr "Dùng kết nối _an toàn:" + +#: ../addressbook/gui/component/ldap-config.glade.h:56 +#: ../addressbook/gui/component/ldap-config.glade.h:53 +msgid "cards" +msgstr "thẻ" + +#: ../glade/search.glade.h:1 ../storage/sunone-permissions-dialog.glade.h:1 +#: ../storage/sunone-subscription-dialog.glade.h:1 po/silky-channel.glade.h:1 +msgid "*" +msgstr "*" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:2 +msgid "Email" +msgstr "Địa chỉ thư" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:3 +#: ../capplets/about-me/gnome-about-me.glade.h:4 +msgid "Home" +msgstr "Ở nhà" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:4 +#: ../capplets/about-me/gnome-about-me.glade.h:5 +msgid "Instant Messaging" +msgstr "Tin nhắn tức khắc" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:5 +msgid "Job" +msgstr "Tác vụ" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:6 +msgid "Miscellaneous" +msgstr "Linh tinh" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:7 +msgid "Other" +msgstr "Khác" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:8 +msgid "Telephone" +msgstr "Điện thoại" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:9 +msgid "Web Addresses" +msgstr "Địa chỉ Mạng" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:10 +#: ../capplets/about-me/gnome-about-me.glade.h:10 +msgid "Work" +msgstr "Chỗ làm" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:184 +msgid "AIM" +msgstr "AIM" + +#: ../addressbook/gui/widgets/e-minicard.c:182 ../main.c:586 ../main.c:1369 +#: ../main.c:1435 ../mimedir/mimedir-vcomponent.c:386 +msgid "Contact" +msgstr "Liên lạc" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2349 +msgid "Contact Editor" +msgstr "Bộ hiệu chỉnh liên lạc" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:14 +msgid "Full _Name..." +msgstr "_Họ tên..." + +#: ../src/Database.cs:804 ../src/Database.cs:823 ../src/Database.cs:841 +#: ../app/interface.c:122 ../app/interface.c:123 +#: ../glade/gbwidgets/gbimage.c:648 ../glom/data_structure/field.cc:558 +#: ../src/glade-gtk.c:2359 ../widgets/gtk+.xml.in.h:100 +#: ../src/orca/rolenames.py:278 +msgid "Image" +msgstr "Ảnh" + +#: ../addressbook/gui/contact-editor/e-contact-editor-im.c:66 +msgid "MSN Messenger" +msgstr "Tin nhắn MSN" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:17 +msgid "Mailing Address" +msgstr "Địa chỉ thư tín" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:18 +msgid "Ni_ckname:" +msgstr "Tên _hiệu :" + +#: ../addressbook/gui/contact-editor/e-contact-editor-im.c:63 +msgid "Novell Groupwise" +msgstr "Phần mềm nhóm Novell" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:21 +#: ../src/prefs.c:771 +msgid "Personal Information" +msgstr "Thông tin cá nhân" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:22 +#: ../sheets/network.sheet.in.h:33 Expense/expense.c:585 +#: Expense/expense.c:1434 +msgid "Telephone" +msgstr "Điện thoại" + +#: ../src/planner-task-view.c:327 ../gncal/todo-categories.c:182 +#: ../ui/user_info.glade.h:67 ../mimedir/mimedir-vcard-address.c:252 +#: ../mimedir/mimedir-vcard-email.c:169 ../mimedir/mimedir-vcard-phone.c:166 +msgid "Work" +msgstr "Chỗ làm" + +#: ../gnomecard/card-editor.glade.h:42 ../pan/dialogs/dialog-newuser.c:389 +#: ../pan/dialogs/dialog-newuser.c:421 ../pan/server-ui.c:333 +msgid "_Address:" +msgstr "_Địa chỉ:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:25 +msgid "_Anniversary:" +msgstr "_Kỷ niệm:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:26 +msgid "_Assistant:" +msgstr "_Phụ tá:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:27 +#: ../ui/user_info.glade.h:71 +msgid "_Birthday:" +msgstr "Ngày _sinh:" + +#: ../data/glade/project-properties.glade.h:7 +msgid "_Calendar:" +msgstr "_Lịch:" + +#: ../ui/evolution-event-editor.xml.h:27 ../ui/evolution-task-editor.xml.h:18 +#: ../glade/straw.glade.h:69 +msgid "_Categories" +msgstr "_Phân loại" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:30 +#: ../ui/user_info.glade.h:72 +msgid "_City:" +msgstr "_Phố :" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:31 +#: ../ui/user_info.glade.h:73 +msgid "_Company:" +msgstr "_Công ty:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:32 +msgid "_Country:" +msgstr "_Quốc gia:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:33 +#: ../ui/user_info.glade.h:74 +msgid "_Department:" +msgstr "_Cơ quan:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:34 +msgid "_File under:" +msgstr "_Tập tin trong:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:35 +msgid "_Free/Busy:" +msgstr "_Rảnh/Bận:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:36 +#: ../ui/user_info.glade.h:79 +msgid "_Home Page:" +msgstr "Trang _chủ :" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:37 +#: ../data/glade/project-properties.glade.h:8 +msgid "_Manager:" +msgstr "Nhà _quản lý:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:38 +msgid "_Notes:" +msgstr "_Ghi chú :" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:39 +msgid "_Office:" +msgstr "_Văn phòng:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:40 +#: ../addressbook/gui/contact-editor/fulladdr.glade.h:7 +msgid "_PO Box:" +msgstr "Hộp _bưu điện:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:41 +#: ../capplets/about-me/gnome-about-me.glade.h:48 +msgid "_Profession:" +msgstr "_Nghề nghiệp:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:42 +msgid "_Spouse:" +msgstr "_Vợ chồng:" + +#: ../capplets/about-me/gnome-about-me.glade.h:50 +msgid "_State/Province:" +msgstr "_Tỉnh/Bang:" + +#: ../plug-ins/imagemap/imap_settings.c:102 ../glade/straw.glade.h:94 +#: ../src/dialog-win.cc:62 ../src/form-editor/form-prop.cc:55 +msgid "_Title:" +msgstr "_Tựa:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:45 +msgid "_Video Chat:" +msgstr "Trò chuyện ảnh _động:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:46 +msgid "_Wants to receive HTML mail" +msgstr "_Muốn nhận thư loại HTML" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:47 +msgid "_Web Log:" +msgstr "_Nhật ký Mạng:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:48 +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:13 +msgid "_Where:" +msgstr "_Nơi:" + +#: ../addressbook/gui/contact-editor/contact-editor.glade.h:49 +msgid "_Zip/Postal Code:" +msgstr "Mã _bưu điện:" + +#: ../gnome-netinfo/lookup.c:308 ../libgnetwork/gnetwork-tcp-connection.c:1368 +#: address_gui.c:2783 prefs_gui.c:370 ../mimedir/mimedir-vcard-email.c:142 +#: ../mimedir/mimedir-vcard.c:368 +msgid "Address" +msgstr "Địa chỉ" + +#: ../widgets/text/e-text.c:3585 +msgid "Editable" +msgstr "Có thể sửa" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:136 +#: Expense/expense.c:132 +msgid "United States" +msgstr "Mỹ" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:138 +#: ../src/util.c:27 +msgid "Afghanistan" +msgstr "A Phú Hãn" + +#: ../boards/geography/board3_1.xml.in.h:1 src/common/util.c:827 +#: ../src/util.c:28 +msgid "Albania" +msgstr "An-ba-ni" + +#: ../boards/geography/board4_2.xml.in.h:3 src/common/util.c:885 +#: ../src/util.c:29 +msgid "Algeria" +msgstr "An-giê-ri" + +#: src/common/util.c:834 ../src/util.c:30 +msgid "American Samoa" +msgstr "Xa-mô-a Mỹ" + +#: src/common/util.c:822 ../src/util.c:31 +msgid "Andorra" +msgstr "An-đoa-ra" + +#: ../src/util.c:32 +msgid "Angola" +msgstr "An-gô-la" + +#: src/common/util.c:826 ../src/util.c:33 +msgid "Anguilla" +msgstr "Ăng-ouí-la" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:145 +msgid "Antarctica" +msgstr "Nam-cực" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:145 +msgid "Antigua And Barbuda" +msgstr "An-ti-gu-a và Ba-bu-đa" + +#: ../src/util.c:35 +msgid "Argentina" +msgstr "Ă-gienh-ti-nạ" + +#: src/common/util.c:828 ../src/util.c:36 +msgid "Armenia" +msgstr "Ac-mê-ni" + +#: src/common/util.c:838 ../src/util.c:37 +msgid "Aruba" +msgstr "Ă-ru-ba" + +#: src/common/util.c:837 ../src/util.c:39 Expense/expense.c:99 +msgid "Australia" +msgstr "Úc" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:151 +#: ../src/util.c:41 Expense/expense.c:100 +msgid "Austria" +msgstr "Áo" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:152 +#: ../src/util.c:42 +msgid "Azerbaijan" +msgstr "A-dợ-bai-sanh" + +#: ../boards/geography/board2_0.xml.in.h:2 src/common/util.c:854 +#: ../src/util.c:43 +msgid "Bahamas" +msgstr "Ba-ha-ma" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:154 +#: ../src/util.c:44 +msgid "Bahrain" +msgstr "Bah-reinh" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:155 +#: ../src/util.c:45 +msgid "Bangladesh" +msgstr "Bang-la-đe-xợ" + +#: src/common/util.c:841 ../src/util.c:46 +msgid "Barbados" +msgstr "Bác-ba-đốt" + +#: ../src/util.c:48 +msgid "Belarus" +msgstr "Be-la-ru-xợ" + +#: ../boards/geography/board3_0.xml.in.h:2 src/common/util.c:843 +#: ../src/util.c:49 Expense/expense.c:101 +msgid "Belgium" +msgstr "Bỉ" + +#: src/common/util.c:859 ../src/util.c:50 +msgid "Belize" +msgstr "Bê-li-xê" + +#: ../boards/geography/board4_2.xml.in.h:5 src/common/util.c:849 +#: ../src/util.c:51 +msgid "Benin" +msgstr "Bê-ninh" + +#: src/common/util.c:850 ../src/util.c:52 +msgid "Bermuda" +msgstr "Be-mư-đa" + +#: src/common/util.c:855 ../src/util.c:53 +msgid "Bhutan" +msgstr "Bu-thăn" + +#: ../boards/geography/board2_1.xml.in.h:2 src/common/util.c:852 +#: ../src/util.c:54 +msgid "Bolivia" +msgstr "Bô-li-vi-a" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:164 +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:163 +msgid "Bosnia And Herzegowina" +msgstr "Boxợ-ni-a và He-de-go-vi-nạ" + +#: ../boards/geography/board4_2.xml.in.h:6 src/common/util.c:857 +#: ../src/util.c:56 +msgid "Botswana" +msgstr "Bốt-xoa-na" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:166 +msgid "Bouvet Island" +msgstr "Đảo Bu-vê" + +#: ../boards/geography/board2_1.xml.in.h:3 src/common/util.c:853 +#: ../src/util.c:57 Expense/expense.c:102 +msgid "Brazil" +msgstr "Bra-xin" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:168 +msgid "British Indian Ocean Territory" +msgstr "Miền Đại dương Ấn-độ quốc Anh" + +#: src/common/util.c:851 +msgid "Brunei Darussalam" +msgstr "Bợru-này Đa-ru-xa-làm" + +#: ../src/util.c:60 +msgid "Bulgaria" +msgstr "Bảo-gai-lơi" + +#: ../boards/geography/board4_2.xml.in.h:7 src/common/util.c:844 +#: ../src/util.c:61 +msgid "Burkina Faso" +msgstr "Buốc-khi-na Pha-xô" + +#: ../boards/geography/board4_2.xml.in.h:8 src/common/util.c:847 +#: ../src/util.c:62 +msgid "Burundi" +msgstr "Bu-run-đi" + +#: ../src/util.c:63 +msgid "Cambodia" +msgstr "Căm Bốt" + +#: ../src/util.c:64 +msgid "Cameroon" +msgstr "Ca-mơ-run" + +#: ../boards/geography/board2_0.xml.in.h:3 src/common/util.c:860 +#: ../src/util.c:65 Expense/expense.c:103 +msgid "Canada" +msgstr "Ca-na-đa" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:176 +msgid "Cape Verde" +msgstr "Cáp-ve-đẹ" + +#: src/common/util.c:950 ../src/util.c:67 +msgid "Cayman Islands" +msgstr "Quần đảo Cay-mạn" + +#: ../src/util.c:68 +msgid "Central African Republic" +msgstr "Cộng hòa Trung Phi" + +#: ../src/util.c:69 +msgid "Chad" +msgstr "Chê-đh" + +#: ../src/util.c:70 +msgid "Chile" +msgstr "Chi-lê" + +#: ../src/util.c:71 +msgid "China" +msgstr "Trung Quốc" + +#: src/common/util.c:877 ../src/util.c:72 +msgid "Christmas Island" +msgstr "Đảo Kh-ri-x-mạ-x" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:182 +msgid "Cocos (Keeling) Islands" +msgstr "Quần đảo Co-co-x (Khi-lịng)" + +#: ../src/util.c:74 +msgid "Colombia" +msgstr "Cô-lôm-bi-a" + +#: ../src/util.c:75 +msgid "Comoros" +msgstr "Co-mo-ro-xợ" + +#: src/common/util.c:864 ../src/util.c:76 +msgid "Congo" +msgstr "Công-gô" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:186 +msgid "Congo, The Democratic Republic Of The" +msgstr "Cộng hoà Dân chủ Công-gô" + +#: src/common/util.c:867 ../src/util.c:77 +msgid "Cook Islands" +msgstr "Quần đảo Khu-kh" + +#: src/common/util.c:873 ../src/util.c:78 +msgid "Costa Rica" +msgstr "Cốt-x-tha Ri-ca" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:190 +msgid "Cote d'Ivoire" +msgstr "Cót đi vouă" + +#: ../src/util.c:79 +msgid "Croatia" +msgstr "Cợ-rô-a-ti-a" + +#: ../boards/geography/board2_0.xml.in.h:4 src/common/util.c:875 +#: ../src/util.c:80 +msgid "Cuba" +msgstr "Cu-ba" + +#: ../src/util.c:81 +msgid "Cyprus" +msgstr "Síp" + +#: src/common/util.c:879 ../src/util.c:82 +msgid "Czech Republic" +msgstr "Cộng hòa Séc" + +#: ../src/util.c:84 Expense/expense.c:104 +msgid "Denmark" +msgstr "Đan-mạch" + +#: ../src/util.c:86 +msgid "Djibouti" +msgstr "Gi-bu-ti" + +#: src/common/util.c:883 ../src/util.c:87 +msgid "Dominica" +msgstr "Đô-mi-ni-cạ" + +#: src/common/util.c:884 ../src/util.c:88 +msgid "Dominican Republic" +msgstr "Cộng hòa Đô-mi-ni-cạ" + +#: ../boards/geography/board2_1.xml.in.h:6 src/common/util.c:886 +#: ../src/util.c:89 +msgid "Ecuador" +msgstr "Ê-cu-a-đoa" + +#: ../src/util.c:90 +msgid "Egypt" +msgstr "Ai-cập" + +#: src/common/util.c:1039 ../src/util.c:91 +msgid "El Salvador" +msgstr "En-san-va-đoa" + +#: ../src/util.c:92 +msgid "Equatorial Guinea" +msgstr "Ghi-nê Xích-đạo" + +#: ../boards/geography/board4_2.xml.in.h:16 src/common/util.c:891 +#: ../src/util.c:93 +msgid "Eritrea" +msgstr "Ê-ri-tơ-rê-a" + +#: ../boards/geography/board3_1.xml.in.h:9 src/common/util.c:888 +#: ../src/util.c:94 +msgid "Estonia" +msgstr "E-xtô-ni-a" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:205 +#: ../src/util.c:95 +msgid "Ethiopia" +msgstr "Ê-ti-ô-pi-a" + +#: ../src/util.c:98 +msgid "Falkland Islands" +msgstr "Quần đảo Phoa-kh-lận" + +#: src/common/util.c:898 +msgid "Faroe Islands" +msgstr "Quần đảo Pha-rô" + +#: src/common/util.c:895 +msgid "Fiji" +msgstr "Phi-gi" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:209 +#: ../src/util.c:100 Expense/expense.c:106 +msgid "Finland" +msgstr "Phần-lan" + +#: ../boards/geography/board3_0.xml.in.h:5 src/common/util.c:899 +#: ../src/util.c:101 Expense/expense.c:107 +msgid "France" +msgstr "Pháp" + +#: src/common/util.c:905 ../src/util.c:103 +msgid "French Guiana" +msgstr "Ghi-a-na Pháp" + +#: src/common/util.c:1004 ../src/util.c:104 +msgid "French Polynesia" +msgstr "Pô-li-nê-di Pháp" + +#: src/common/util.c:1044 +msgid "French Southern Territories" +msgstr "Miền Nam Pháp" + +#: ../boards/geography/board4_2.xml.in.h:17 src/common/util.c:901 +#: ../src/util.c:105 +msgid "Gabon" +msgstr "Ga-bông" + +#: ../boards/geography/board4_2.xml.in.h:18 src/common/util.c:910 +#: ../src/util.c:106 +msgid "Gambia" +msgstr "Găm-bi-a" + +#: src/common/util.c:904 ../src/util.c:107 +msgid "Georgia" +msgstr "Gi-oa-gi-a" + +#: ../boards/geography/board3_0.xml.in.h:6 src/common/util.c:880 +#: ../src/util.c:108 Expense/expense.c:108 +msgid "Germany" +msgstr "Đức" + +#: ../src/util.c:109 +msgid "Ghana" +msgstr "Gă-na" + +#: src/common/util.c:908 ../src/util.c:110 +msgid "Gibraltar" +msgstr "Gi-boa-tha" + +#: ../src/util.c:111 +msgid "Greece" +msgstr "Hy-lạp" + +#: ../src/util.c:112 +msgid "Greenland" +msgstr "Đảo băng" + +#: ../src/util.c:113 +msgid "Grenada" +msgstr "Gợ-rê-nă-đa" + +#: src/common/util.c:913 ../src/util.c:114 +msgid "Guadeloupe" +msgstr "Gu-a-đe-luc" + +#: src/common/util.c:918 ../src/util.c:115 +msgid "Guam" +msgstr "Gu-ăm" + +#: src/common/util.c:917 ../src/util.c:117 +msgid "Guatemala" +msgstr "Gua-tê-ma-la" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:226 +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:225 +msgid "Guernsey" +msgstr "Gơnh-di" + +#: ../boards/geography/board4_2.xml.in.h:20 src/common/util.c:911 +#: ../src/util.c:118 +msgid "Guinea" +msgstr "Ghi-nê" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:228 +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:227 +msgid "Guinea-bissau" +msgstr "Ghi-nê-bi-sau" + +#: ../boards/geography/board2_1.xml.in.h:8 src/common/util.c:920 +#: ../src/util.c:120 +msgid "Guyana" +msgstr "Guy-a-na" + +#: ../boards/geography/board2_0.xml.in.h:7 src/common/util.c:925 +#: ../src/util.c:121 +msgid "Haiti" +msgstr "Ha-i-ti" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:230 +msgid "Heard And McDonald Islands" +msgstr "Quần đảo Hơd và Mợc-đo-nợd" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:231 +msgid "Holy See" +msgstr "Toà thánh" + +#: src/common/util.c:923 ../src/util.c:122 +msgid "Honduras" +msgstr "Hôn-đu-rát" + +#: ../src/util.c:123 Expense/expense.c:109 +msgid "Hong Kong" +msgstr "Hồng Kông" + +#: ../src/util.c:124 +msgid "Hungary" +msgstr "Hung-gia-lợi" + +#: ../src/util.c:130 Expense/expense.c:110 +msgid "Iceland" +msgstr "Băng-đảo" + +#: src/common/util.c:930 ../src/util.c:131 Expense/expense.c:111 +msgid "India" +msgstr "Ấn-độ" + +#: src/common/util.c:927 ../src/util.c:132 Expense/expense.c:112 +#, fuzzy +msgid "Indonesia" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Nam Dương\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"Nam-dương" + +#: ../src/util.c:134 +msgid "Iran" +msgstr "Ba-tư" + +#: src/common/util.c:934 ../src/util.c:135 +msgid "Iraq" +msgstr "I-rắc" + +#: ../src/util.c:136 Expense/expense.c:113 +msgid "Ireland" +msgstr "Ái-nhĩ-lan" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:241 +msgid "Isle of Man" +msgstr "Đảo Man" + +#: src/common/util.c:929 ../src/util.c:137 +msgid "Israel" +msgstr "Do-thái" + +#: ../src/util.c:138 Expense/expense.c:114 +msgid "Italy" +msgstr "Ý" + +#: ../src/util.c:140 +msgid "Jamaica" +msgstr "Gia-mê-ca" + +#: src/common/util.c:940 ../src/util.c:141 Expense/expense.c:115 +msgid "Japan" +msgstr "Nhật-bản" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:246 +msgid "Jersey" +msgstr "Chơ-di" + +#: src/common/util.c:939 ../src/util.c:142 +msgid "Jordan" +msgstr "Gi-oa-đan" + +#: src/common/util.c:951 ../src/util.c:143 +msgid "Kazakhstan" +msgstr "Ca-da-kh-x-than" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:250 +#: ../src/util.c:144 +msgid "Kenya" +msgstr "Khi-ni-a" + +#: src/common/util.c:944 +msgid "Kiribati" +msgstr "Ki-ri-ba-ti" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:252 +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:251 +msgid "Korea, Democratic People's Republic Of" +msgstr "Cộng hoà Nhân dân Dân chủ Triều tiên" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:253 +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:252 +msgid "Korea, Republic Of" +msgstr "Cộng hoà Triều tiên" + +#: src/common/util.c:949 ../src/util.c:148 +msgid "Kuwait" +msgstr "Cu-ouai-th" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:255 +msgid "Kyrgyzstan" +msgstr "Cơ-chi-x-tănh" + +#: src/common/util.c:952 ../src/util.c:150 +msgid "Laos" +msgstr "Lào" + +#: ../src/util.c:151 +msgid "Latvia" +msgstr "Lát-vi-a" + +#: src/common/util.c:953 ../src/util.c:152 +msgid "Lebanon" +msgstr "Le-ba-non" + +#: ../src/util.c:153 +msgid "Lesotho" +msgstr "Le-xô-tô" + +#: ../src/util.c:154 +msgid "Liberia" +msgstr "Li-bê-ri-a" + +#: ../src/util.c:155 +msgid "Libya" +msgstr "Li-bi-a" + +#: ../src/util.c:156 +msgid "Liechtenstein" +msgstr "Likh-ten-sợ-tâynh" + +#: ../src/util.c:157 +msgid "Lithuania" +msgstr "Li-tu-a-ni" + +#: ../src/util.c:158 Expense/expense.c:117 +msgid "Luxembourg" +msgstr "Lúc-xăm-buac" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:264 +msgid "Macao" +msgstr "Ma-cao" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:266 +msgid "Macedonia" +msgstr "Ma-xe-đô-ni-a" + +#: ../src/util.c:160 +msgid "Madagascar" +msgstr "Ma-đa-ga-x-că" + +#: ../src/util.c:161 +msgid "Malawi" +msgstr "Ma-la-uy" + +#: src/common/util.c:984 ../src/util.c:162 Expense/expense.c:118 +msgid "Malaysia" +msgstr "Ma-lay-xi-a" + +#: src/common/util.c:981 ../src/util.c:163 +msgid "Maldives" +msgstr "Mal-đi-vợx" + +#: ../boards/geography/board4_2.xml.in.h:29 src/common/util.c:971 +#: ../src/util.c:164 +msgid "Mali" +msgstr "Ma-li" + +#: ../src/util.c:165 +msgid "Malta" +msgstr "Moa-ta" + +#: src/common/util.c:968 ../src/util.c:166 +msgid "Marshall Islands" +msgstr "Quần đảo Mác-san" + +#: src/common/util.c:976 ../src/util.c:167 +msgid "Martinique" +msgstr "Mác-thi-ni-kh" + +#: ../src/util.c:168 +msgid "Mauritania" +msgstr "Mô-ri-ta-ni-a" + +#: ../src/util.c:169 +msgid "Mauritius" +msgstr "Mâu-ri-sơ-x" + +#: src/common/util.c:1075 +msgid "Mayotte" +msgstr "May-oth" + +#: ../src/util.c:171 Expense/expense.c:119 +msgid "Mexico" +msgstr "Mê-hi-cô" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:279 +msgid "Micronesia" +msgstr "Mi-cợ-rô-nê-xi-a" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:279 +msgid "Moldova, Republic Of" +msgstr "Cộng Hòa Mon-đô-va" + +#: src/common/util.c:964 ../src/util.c:174 +msgid "Monaco" +msgstr "Mô-na-cô" + +#: ../src/util.c:175 +msgid "Mongolia" +msgstr "Mông-cổ" + +#: src/common/util.c:978 ../src/util.c:176 +msgid "Montserrat" +msgstr "Mon-xe-rạc" + +#: src/common/util.c:963 ../src/util.c:177 +msgid "Morocco" +msgstr "Ma-rốc" + +#: ../src/util.c:178 +msgid "Mozambique" +msgstr "Mô-dăm-bích" + +#: ../src/util.c:179 +msgid "Myanmar" +msgstr "Miến-điện" + +#: ../src/util.c:180 +msgid "Namibia" +msgstr "Na-mi-bi-a" + +#: src/common/util.c:996 ../src/util.c:181 +msgid "Nauru" +msgstr "Nau-ru" + +#: ../src/gcompris/config.c:89 src/common/util.c:995 ../src/util.c:182 +msgid "Nepal" +msgstr "Nê-pan" + +#: src/common/util.c:993 ../src/util.c:184 Expense/expense.c:120 +msgid "Netherlands" +msgstr "Hoà-lan" + +#: ../src/util.c:183 +msgid "Netherlands Antilles" +msgstr "An-thi-le-x Hoà-lan" + +#: src/common/util.c:987 ../src/util.c:186 +msgid "New Caledonia" +msgstr "Niu Ca-lê-đô-ni-a" + +#: src/common/util.c:999 ../src/util.c:187 Expense/expense.c:121 +msgid "New Zealand" +msgstr "Niu Di-lân" + +#: src/common/util.c:992 ../src/util.c:188 +msgid "Nicaragua" +msgstr "Ni-ca-ra-gua" + +#: ../boards/geography/board4_2.xml.in.h:34 src/common/util.c:988 +#: ../src/util.c:189 +msgid "Niger" +msgstr "Ni-giê" + +#: ../boards/geography/board4_2.xml.in.h:35 src/common/util.c:991 +#: ../src/util.c:190 +msgid "Nigeria" +msgstr "Ni-giê-ri-a" + +#: src/common/util.c:998 ../src/util.c:191 +msgid "Niue" +msgstr "Ni-u-e" + +#: src/common/util.c:990 ../src/util.c:192 +msgid "Norfolk Island" +msgstr "Đảo Noa-phực" + +#: src/common/util.c:975 +msgid "Northern Mariana Islands" +msgstr "Quần đảo Ma-ri-a-na Bắc" + +#: ../src/util.c:193 Expense/expense.c:122 +msgid "Norway" +msgstr "Na-uy" + +#: src/common/util.c:1000 ../src/util.c:194 +msgid "Oman" +msgstr "Ô-man" + +#: src/common/util.c:1007 ../src/util.c:195 +msgid "Pakistan" +msgstr "Ba-ki-x-thănh" + +#: src/common/util.c:1014 ../src/util.c:196 +msgid "Palau" +msgstr "Ba-lau" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:304 +msgid "Palestinian Territory" +msgstr "Lãnh thổ Pa-le-x-tính" + +#: src/common/util.c:1002 ../src/util.c:197 +msgid "Panama" +msgstr "Ba-na-ma" + +#: src/common/util.c:1005 ../src/util.c:198 +msgid "Papua New Guinea" +msgstr "Pa-pu-a Niu Ghi-nê" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:307 +#: src/common/util.c:1015 ../src/util.c:199 +msgid "Paraguay" +msgstr "Ba-ra-guay" + +#: ../boards/geography/board2_1.xml.in.h:11 src/common/util.c:1003 +#: ../src/util.c:200 +msgid "Peru" +msgstr "Pê-ru" + +#: src/common/util.c:1006 ../src/util.c:201 Expense/expense.c:124 +msgid "Philippines" +msgstr "Phi-luật-tân" + +#: src/common/util.c:1010 +msgid "Pitcairn" +msgstr "Bi-th-khenh" + +#: ../boards/geography/board3_1.xml.in.h:16 src/common/util.c:1008 +#: ../src/util.c:202 +msgid "Poland" +msgstr "Ba-lan" + +#: ../src/util.c:203 +msgid "Portugal" +msgstr "Bồ-đào-nha" + +#: src/common/util.c:1011 ../src/util.c:204 +msgid "Puerto Rico" +msgstr "Bu-éc-thô Ri-cô" + +#: src/common/util.c:1016 ../src/util.c:205 +msgid "Qatar" +msgstr "Ca-tă" + +#: src/common/util.c:1017 +msgid "Reunion" +msgstr "Rê-u-ni-ợnh" + +#: ../src/util.c:207 +msgid "Romania" +msgstr "Lỗ-má-ni" + +#: src/common/util.c:1020 +msgid "Russian Federation" +msgstr "Liên bang Nga" + +#: ../boards/geography/board4_2.xml.in.h:36 src/common/util.c:1021 +#: ../src/util.c:210 +msgid "Rwanda" +msgstr "Ru-oanh-đa" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:318 +msgid "Saint Kitts And Nevis" +msgstr "Xan Khi-th-x và Ne-vi-x" + +#: ../src/util.c:211 +msgid "Saint Lucia" +msgstr "Xan Lu-xi-a" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:320 +msgid "Saint Vincent And The Grenadines" +msgstr "Xan Vinh-xen và Gợ-re-na-đính" + +#: src/common/util.c:1073 +msgid "Samoa" +msgstr "Xa-moa" + +#: src/common/util.c:1033 ../src/util.c:213 +msgid "San Marino" +msgstr "Xan Ma-ri-nô" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:323 +msgid "Sao Tome And Principe" +msgstr "Xao Tô-mê và Pợ-rinh-xi-pê" + +#: src/common/util.c:1022 ../src/util.c:215 +msgid "Saudi Arabia" +msgstr "A-rập Xau-đi" + +#: ../boards/geography/board4_2.xml.in.h:37 src/common/util.c:1034 +msgid "Senegal" +msgstr "Xê-nê-gan" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:326 +msgid "Serbia And Montenegro" +msgstr "Xéc-bi và Mon-the-nê-gợ-rô" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:328 +msgid "Seychelles" +msgstr "Xê-sen" + +#: ../src/util.c:218 +msgid "Sierra Leone" +msgstr "Xi-ê-ra Lê-ôn" + +#: src/common/util.c:1027 ../src/util.c:219 Expense/expense.c:125 +msgid "Singapore" +msgstr "Xin-ga-po" + +#: ../boards/geography/board3_1.xml.in.h:20 +msgid "Slovakia" +msgstr "Xlô-vác" + +#: ../boards/geography/board3_1.xml.in.h:21 src/common/util.c:1029 +#: ../src/util.c:221 +msgid "Slovenia" +msgstr "Xlô-ven" + +#: ../src/util.c:222 +msgid "Solomon Islands" +msgstr "Quần đảo Xô-lô-mông" + +#: ../src/util.c:223 +msgid "Somalia" +msgstr "Xo-ma-li" + +#: ../boards/geography/board4_2.xml.in.h:40 src/common/util.c:1077 +#: ../src/util.c:224 +msgid "South Africa" +msgstr "Nam Phi" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:335 +msgid "South Georgia And The South Sandwich Islands" +msgstr "Quần đảo Gi-oa-gi-a và Nam Xan-oui-ch" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:337 +#: ../src/util.c:225 Expense/expense.c:126 +msgid "Spain" +msgstr "Tây-ban-nha" + +#: ../src/util.c:226 +msgid "Sri Lanka" +msgstr "Tích-lan" + +#: ../src/util.c:227 +msgid "St. Helena" +msgstr "Xan He-lê-na" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:339 +msgid "St. Pierre And Miquelon" +msgstr "Xan Pi-e và Mi-quê-lon" + +#: ../src/util.c:231 +msgid "Sudan" +msgstr "Xu-đănh" + +#: ../src/util.c:232 +msgid "Suriname" +msgstr "Xu-ri-năm" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:342 +msgid "Svalbard And Jan Mayen Islands" +msgstr "Quần đảo X-văn-băn và Dăn May-en" + +#: ../src/util.c:233 +msgid "Swaziland" +msgstr "Xouă-di-lạn" + +#: ../src/util.c:234 Expense/expense.c:127 +msgid "Sweden" +msgstr "Thụy-điển" + +#: ../src/util.c:235 Expense/expense.c:128 +msgid "Switzerland" +msgstr "Thụy-sĩ" + +#: ../src/util.c:236 +msgid "Syria" +msgstr "Xi-ri-a" + +#: src/common/util.c:1056 Expense/expense.c:129 +#, fuzzy +msgid "Taiwan" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Đài Loan\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"Đài-loan" + +#: ../src/util.c:238 +msgid "Tajikistan" +msgstr "Tha-dikh-x-thăn" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:349 +msgid "Tanzania, United Republic Of" +msgstr "Cộng hoà Thông nhất Thăn-da-ni-a" + +#: ../src/util.c:240 Expense/expense.c:130 +msgid "Thailand" +msgstr "Thái-lan" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:351 +msgid "Timor-Leste" +msgstr "Thi-moa Lex-the" + +#: ../boards/geography/board4_2.xml.in.h:43 src/common/util.c:1045 +#: ../src/util.c:242 +msgid "Togo" +msgstr "Tô-gô" + +#: src/common/util.c:1048 ../src/util.c:243 +msgid "Tokelau" +msgstr "To-ke-lau" + +#: src/common/util.c:1051 ../src/util.c:244 +msgid "Tonga" +msgstr "Tông-ga" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:355 +msgid "Trinidad And Tobago" +msgstr "Tợ-ri-ni-đat và To-ba-gô" + +#: ../boards/geography/board4_2.xml.in.h:44 src/common/util.c:1050 +#: ../src/util.c:246 +msgid "Tunisia" +msgstr "Tu-ni-xi-a" + +#: ../src/util.c:247 +msgid "Turkey" +msgstr "Thổ-nhĩ-kỳ" + +#: ../src/util.c:248 +msgid "Turkmenistan" +msgstr "Thua-khợ-me-ni-x-tănh" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:359 +msgid "Turks And Caicos Islands" +msgstr "Quần Thổ-kh-x và Cai-co-x" + +#: src/common/util.c:1055 ../src/util.c:250 +msgid "Tuvalu" +msgstr "Tu-va-lu" + +#: ../src/util.c:252 +msgid "Uganda" +msgstr "U-găn-đa" + +#: ../src/util.c:253 +msgid "Ukraine" +msgstr "U-cợ-rainh" + +#: src/common/util.c:823 ../src/util.c:254 +msgid "United Arab Emirates" +msgstr "Các Tiểu Vương quốc A-rập Thống nhất" + +#: src/common/util.c:1060 ../src/util.c:255 Expense/expense.c:131 +msgid "United Kingdom" +msgstr "Vương quốc Anh Thống nhất" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:365 +msgid "United States Minor Outlying Islands" +msgstr "Quần đảo ở xa nhỏ Mỹ" + +#: ../src/util.c:257 +msgid "Uruguay" +msgstr "U-ru-guay" + +#: ../src/util.c:258 +msgid "Uzbekistan" +msgstr "U-dợ-be-ki-x-thăn" + +#: src/common/util.c:1071 ../src/util.c:259 +msgid "Vanuatu" +msgstr "Va-nu-a-tu" + +#: ../src/util.c:261 +msgid "Venezuela" +msgstr "Vê-nê-du-ê-la" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:370 +msgid "Viet Nam" +msgstr "Việt Nam" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:371 +msgid "Virgin Islands, British" +msgstr "Quần đảo Vơ-ginh Anh" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:372 +msgid "Virgin Islands, U.S." +msgstr "Quần đảo Vơ-ginh Mỹ" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:373 +msgid "Wallis And Futuna Islands" +msgstr "Quần đảo Oua-lit và Phu-tu-na" + +#: ../addressbook/gui/contact-editor/e-contact-editor-address.c:375 +msgid "Western Sahara" +msgstr "Tây Sa-ha-ra" + +#: ../src/util.c:265 +msgid "Yemen" +msgstr "Y-ê-men" + +#: ../boards/geography/board4_2.xml.in.h:46 src/common/util.c:1078 +#: ../src/util.c:267 +msgid "Zambia" +msgstr "Dăm-bi-a" + +#: ../boards/geography/board4_2.xml.in.h:47 src/common/util.c:1079 +#: ../src/util.c:268 +msgid "Zimbabwe" +msgstr "Dim-ba-bu-ê" + +#: ../addressbook/gui/contact-editor/e-contact-editor-im.c:62 +msgid "AOL Instant Messenger" +msgstr "Tin nhắn AOL" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:185 +msgid "Jabber" +msgstr "Jabber" + +#: ../addressbook/gui/contact-editor/e-contact-editor-im.c:65 +msgid "Yahoo Messenger" +msgstr "Tin nhắn Yahoo" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:188 ../src/prefs.c:134 +msgid "ICQ" +msgstr "ICQ" + +#: ../gnome-netinfo/scan.c:297 +msgid "Service" +msgstr "Dịch vụ" + +#: feededit.c:361 ../libgda/gda-config.c:1570 ../testing/gda-diagnose.c:282 +#: schroot/sbuild-chroot-plain.cc:112 +msgid "Location" +msgstr "Địa điểm" + +#: src/common/text.c:634 ../src/dialogs.c:1487 ../libgda/gda-config.c:1867 +msgid "Username" +msgstr "Tên người dùng" + +#: ../mimedir/mimedir-vcard-address.c:246 ../mimedir/mimedir-vcard-email.c:163 +#: ../mimedir/mimedir-vcard-phone.c:160 +msgid "Home" +msgstr "Nhà" + +#: web/template/resources_edit_main.tpl:112 ../src/util.c:459 +#: ../src/util.c:517 src/chfn.c:194 address_gui.c:2791 Expense/expense.c:571 +#: Expense/expense.c:1427 libexif/exif-entry.c:433 libexif/exif-entry.c:460 +msgid "Other" +msgstr "Khác" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:186 +msgid "Yahoo" +msgstr "Yahoo" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:187 +msgid "MSN" +msgstr "MSN" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:189 +msgid "GroupWise" +msgstr "GroupWise" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:258 +msgid "Source Book" +msgstr "Sổ nguồn" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:265 +msgid "Target Book" +msgstr "Sổ đích" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:279 +msgid "Is New Contact" +msgstr "Là Liên lạc mới" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:286 +msgid "Writable Fields" +msgstr "Trường có thể ghi" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:293 +msgid "Required Fields" +msgstr "Trường cần thiết" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:307 main.c:1603 +msgid "Changed" +msgstr "Đã đổi" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2344 +#, c-format +msgid "Contact Editor - %s" +msgstr "Bộ hiệu chỉnh liên lạc — « %s »" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2650 +msgid "Please select an image for this contact" +msgstr "Hãy chọn ảnh cho liên lạc này" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2688 +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2651 +msgid "No image" +msgstr "Không ảnh" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2967 +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2927 +msgid "" +"The contact data is invalid:\n" +"\n" +msgstr "" +"Dữ liệu liên lạc không hợp lệ:\n" +"\n" + +#: ../addressbook/gui/contact-editor/e-contact-editor.c:3019 +#: ../addressbook/gui/contact-editor/e-contact-editor.c:2979 +msgid "Invalid contact." +msgstr "Liên lạc không hợp lệ." + +#: ../addressbook/gui/contact-editor/e-contact-quick-add.c:277 +msgid "Contact Quick-Add" +msgstr "Thêm nhanh liên lạc" + +#: ../addressbook/gui/contact-editor/e-contact-quick-add.c:280 +msgid "_Edit Full" +msgstr "_Sửa đổi toàn bộ" + +#: ../addressbook/gui/contact-editor/e-contact-quick-add.c:306 +msgid "_Full name:" +msgstr "_Họ tên:" + +#: ../addressbook/gui/contact-editor/e-contact-quick-add.c:316 +msgid "E-_mail:" +msgstr "Th_ư điện tử :" + +#: ../addressbook/gui/contact-editor/eab-editor.c:323 +#, c-format +msgid "" +"Are you sure you want\n" +"to delete contact list (%s) ?" +msgstr "" +"Bạn có chắc muốn xoá bỏ\n" +"danh sách liên lạc (« %s ») không?" + +#: ../addressbook/gui/contact-editor/eab-editor.c:326 +msgid "" +"Are you sure you want\n" +"to delete these contact lists?" +msgstr "" +"Bạn có chắc muốn xoá bỏ\n" +"những danh sách liên lạc này không?" + +#: ../addressbook/gui/contact-editor/eab-editor.c:331 +#, c-format +msgid "" +"Are you sure you want\n" +"to delete contact (%s) ?" +msgstr "" +"Bạn có chắc muốn xoá bỏ\n" +"liên lạc (« %s ») không?" + +#: ../addressbook/gui/contact-editor/eab-editor.c:334 +msgid "" +"Are you sure you want\n" +"to delete these contacts?" +msgstr "" +"Bạn có chắc muốn xoá bỏ\n" +"những liên lạc này không?" + +#: ../addressbook/gui/contact-editor/fulladdr.glade.h:2 +msgid "Address _2:" +msgstr "Địa chỉ _2:" + +#: ../addressbook/gui/contact-editor/fulladdr.glade.h:3 +#: ../capplets/about-me/gnome-about-me.glade.h:21 +msgid "Ci_ty:" +msgstr "_Phố :" + +#: ../addressbook/gui/contact-editor/fulladdr.glade.h:4 +msgid "Countr_y:" +msgstr "_Quốc gia:" + +#: ../addressbook/gui/contact-editor/fulladdr.glade.h:5 +msgid "Full Address" +msgstr "Địa chỉ đầy đủ" + +#: ../addressbook/gui/contact-editor/fulladdr.glade.h:9 +msgid "_ZIP Code:" +msgstr "Mã _bữu điện:" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:2 ../namedetail.c:30 +msgid "Dr." +msgstr "TS." + +#: ../addressbook/gui/contact-editor/fullname.glade.h:3 ../namedetail.c:32 +msgid "Esq." +msgstr "Esq." + +#: ../addressbook/gui/contact-editor/fullname.glade.h:4 +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:15 src/chfn.c:142 +#: web/template/editaccount_main.tpl:2 web/template/newaccount_main.tpl:2 +#: src/chfn.c:174 +msgid "Full Name" +msgstr "Họ tên" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:5 +#: ../gnopi/cmdmapui.c:151 +msgid "I" +msgstr "I" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:6 ../namedetail.c:32 +msgid "II" +msgstr "II" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:7 ../namedetail.c:32 +msgid "III" +msgstr "III" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:8 ../namedetail.c:32 +msgid "Jr." +msgstr "Con." + +#: ../addressbook/gui/contact-editor/fullname.glade.h:9 ../namedetail.c:30 +msgid "Miss" +msgstr "Cô" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:10 ../namedetail.c:30 +msgid "Mr." +msgstr "Ông" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:11 ../namedetail.c:30 +msgid "Mrs." +msgstr "Bà" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:12 ../namedetail.c:30 +msgid "Ms." +msgstr "Cô/Bà" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:13 ../namedetail.c:32 +msgid "Sr." +msgstr "Ông" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:14 +msgid "_First:" +msgstr "_Tên:" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:15 +msgid "_Last:" +msgstr "_Họ :" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:16 +msgid "_Middle:" +msgstr "Tên _lót:" + +#: ../addressbook/gui/contact-editor/fullname.glade.h:17 +#: ../gnomecard/card-editor.glade.h:62 +msgid "_Suffix:" +msgstr "_Hậu tố :" + +#: ../addressbook/gui/contact-editor/im.glade.h:2 +msgid "Add IM Account" +msgstr "Thêm tài khoản tin nhắn" + +#: ../addressbook/gui/contact-editor/im.glade.h:3 +msgid "_Account name:" +msgstr "Tên tài _khoản:" + +#: ../addressbook/gui/contact-editor/im.glade.h:4 +msgid "_IM Service:" +msgstr "Dịch vụ t_in nhắn:" + +#: ../gtk/gtkfilechooserdefault.c:7271 ../src/drivel.glade.h:72 +#: ../glade/straw.glade.h:80 +msgid "_Location:" +msgstr "_Địa điểm:" + +#: ../src/f-spot.glade.h:1 ogginfo/ogginfo2.c:365 +#, c-format +msgid "\n" +msgstr "\n" + +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:4 +msgid "Add an email to the List" +msgstr "Thêm một địa chỉ thư điện tử vào danh sách" + +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:817 +msgid "Contact List Editor" +msgstr "Bộ hiệu chỉnh danh sách liên lạc" + +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:6 +msgid "Insert email addresses from Address Book" +msgstr "Chèn địa chỉ thư điện tử từ Sổ địa chỉ" + +#: ../symbol-browser-control/symbol-browser.c:72 +msgid "Members" +msgstr "Thành viên" + +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:8 +msgid "Remove an email address from the List" +msgstr "Gỡ bỏ địa chỉ thư điện tử khỏi danh sách" + +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:9 +msgid "_Hide addresses when sending mail to this list" +msgstr "Ẩ_n các địa chỉ khi gởi thư tới danh sách" + +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:10 +msgid "_List name:" +msgstr "Tên _danh sách:" + +#: ../app/actions/select-actions.c:47 src/gtkam-main.c:561 +#: ../src/glade-popup.c:274 +msgid "_Select" +msgstr "_Chọn" + +#: ../addressbook/gui/contact-list-editor/contact-list-editor.glade.h:12 +msgid "_Type an email address or drag a contact into the list below:" +msgstr "_Nhập địa chỉ thư hoặc kéo liên lạc vào danh sách dưới đây:" + +#: ../addressbook/gui/widgets/e-minicard-view.c:505 +msgid "Book" +msgstr "Sổ" + +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:177 +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:176 +msgid "Is New List" +msgstr "Là danh sách mới" + +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:719 +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:707 +msgid "_Members" +msgstr "Thành _viên" + +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:722 +#: ../addressbook/gui/contact-list-editor/e-contact-list-editor.c:710 +msgid "Contact List Members" +msgstr "Thành viên danh sách" + +#: ../addressbook/gui/merging/eab-contact-commit-duplicate-detected.glade.h:1 +msgid "Changed Contact:" +msgstr "Liên lạc đã đổi:" + +#: ../addressbook/gui/merging/eab-contact-commit-duplicate-detected.glade.h:2 +msgid "Conflicting Contact:" +msgstr "Liên lạc xung đột:" + +#: ../addressbook/gui/merging/eab-contact-commit-duplicate-detected.glade.h:3 +#: ../addressbook/gui/merging/eab-contact-duplicate-detected.glade.h:1 +msgid "Duplicate Contact Detected" +msgstr "Phát hiện liên lạc trùng" + +#: ../addressbook/gui/merging/eab-contact-commit-duplicate-detected.glade.h:4 +msgid "" +"The changed email or name of this contact already\n" +"exists in this folder. Would you like to add it anyway?" +msgstr "" +"Tên hoặc địa chỉ thư điện tử đã thay đổi của liên lạc này\n" +"đã có trong thư mục này. Bạn vẫn có muốn thêm không?" + +#: ../addressbook/gui/merging/eab-contact-duplicate-detected.glade.h:2 +msgid "New Contact:" +msgstr "Liên lạc mới:" + +#: ../addressbook/gui/merging/eab-contact-duplicate-detected.glade.h:3 +msgid "Original Contact:" +msgstr "Liên lạc gốc:" + +#: ../addressbook/gui/merging/eab-contact-duplicate-detected.glade.h:4 +msgid "" +"The name or email address of this contact already exists\n" +"in this folder. Would you like to add it anyway?" +msgstr "" +"Tên hoặc địa chỉ thư điện từ của liên lạc này đã có\n" +"trong thư mục này. Bạn vẫn có muốn thêm không?" + +#: ../widgets/misc/e-filter-bar.c:156 +msgid "Advanced Search" +msgstr "Tìm kiếm cấp cao" + +#: ../addressbook/gui/widgets/e-addressbook-model.c:148 +msgid "No contacts" +msgstr "Không có liên lạc" + +#: ../addressbook/gui/widgets/e-addressbook-model.c:151 +#, c-format +msgid "%d contact" +msgid_plural "%d contact" +msgstr[0] "%d liên lạc" + +#: ../addressbook/gui/widgets/e-addressbook-model.c:446 +msgid "Error getting book view" +msgstr "Gập lỗi khi gọi khung xem sổ" + +#: src/set_data.c:314 libexif/exif-tag.c:105 +msgid "Model" +msgstr "Mô hình" + +#: ../addressbook/gui/widgets/e-addressbook-table-adapter.c:103 +msgid "Error modifying card" +msgstr "Gặp lỗi khi sửa đổi thẻ" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:170 +#: ../addressbook/gui/widgets/e-addressbook-view.c:168 +msgid "Name begins with" +msgstr "Tên bắt đầu bằng" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:171 +#: ../addressbook/gui/widgets/e-addressbook-view.c:169 +msgid "Email begins with" +msgstr "Thư bắt đầu bằng" + +#: ../calendar/gui/cal-search-bar.c:53 +msgid "Category is" +msgstr "Phân loại là" + +#: ../calendar/gui/cal-search-bar.c:48 +msgid "Any field contains" +msgstr "Bất kỳ trường nào chứa" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:177 +msgid "Advanced..." +msgstr "Cấp cao..." + +#: ../libgnomedb/gnome-db-error.c:231 ../app/tools/gimpclonetool.c:329 +msgid "Source" +msgstr "Nguồn" + +#: ../ui/evolution-addressbook.xml.h:19 +msgid "Save as VCard..." +msgstr "Lưu dạng vCard..." + +#: ../addressbook/gui/widgets/e-addressbook-view.c:946 +msgid "_New Contact..." +msgstr "Liên lạc _mới..." + +#: ../addressbook/gui/widgets/e-addressbook-view.c:947 +msgid "New Contact _List..." +msgstr "_Danh sách liên lạc mới..." + +#: ../addressbook/gui/widgets/e-addressbook-view.c:950 +#: ../ui/evolution-addressbook.xml.h:41 +msgid "_Save as VCard..." +msgstr "Lư_u dạng vCard..." + +#: ../addressbook/gui/widgets/e-addressbook-view.c:951 +msgid "_Forward Contact" +msgstr "_Chuyển tiếp liên lạc" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:952 +msgid "_Forward Contacts" +msgstr "_Chuyển tiếp các liên lạc" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:953 +msgid "Send _Message to Contact" +msgstr "Gởi th_ư cho liên lạc" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:954 +msgid "Send _Message to List" +msgstr "Gởi th_ư cho danh sách" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:955 +msgid "Send _Message to Contacts" +msgstr "Gởi th_ư cho các liên lạc" + +#: ../plug-ins/common/winprint.c:224 +msgid "_Print" +msgstr "_In" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:959 +msgid "Cop_y to Address Book..." +msgstr "_Chép vào Sổ địa chỉ..." + +#: ../addressbook/gui/widgets/e-addressbook-view.c:960 +msgid "Mo_ve to Address Book..." +msgstr "Chu_yển vào Sổ địa chỉ..." + +#: ../app/actions/edit-actions.c:86 +msgid "Cu_t" +msgstr "Cắ_t" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:965 app/menubar.c:520 +#, fuzzy +msgid "P_aste" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"_Dán\n" +"#-#-#-#-# soundtracker-0.6.7.vi.po (soundtracker) #-#-#-#-#\n" +"D_án" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:1561 +msgid "Any Category" +msgstr "Bất kỳ phân loại nào" + +#: ../addressbook/gui/widgets/e-addressbook-view.c:1760 +msgid "Print cards" +msgstr "In các thẻ" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:1 +#: ../addressbook/libebook/e-contact.c:208 +msgid "Assistant" +msgstr "Phụ tá" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:2 +#: ../addressbook/libebook/e-contact.c:126 +msgid "Assistant Phone" +msgstr "Điện thoại phụ tá" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:3 +#: ../addressbook/libebook/e-contact.c:129 +msgid "Business Fax" +msgstr "Điện thư kinh doanh" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:4 +#: ../addressbook/libebook/e-contact.c:127 +msgid "Business Phone" +msgstr "Điện thoại kinh doanh" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:5 +#: ../addressbook/libebook/e-contact.c:128 +msgid "Business Phone 2" +msgstr "Điện thoại kinh doanh 2" + +#: ../addressbook/libebook/e-contact.c:130 +msgid "Callback Phone" +msgstr "Số gọi lại" + +#: ../addressbook/libebook/e-contact.c:131 +msgid "Car Phone" +msgstr "Điện thoại xe" + +#: ../list-ui.c:653 ../gncal/todo-list.c:1095 src/prefsdlg.cpp:230 +#: ../mimedir/mimedir-vcard.c:493 +msgid "Categories" +msgstr "Phân loại" + +#: ../addressbook/libebook/e-contact.c:132 +msgid "Company Phone" +msgstr "Điện thoại công ty" + +#: src/dictmanagedlg.cpp:507 +msgid "Email" +msgstr "Thư điện tử" + +#: ../addressbook/libebook/e-contact.c:149 +msgid "Email 2" +msgstr "Thư điện tử 2" + +#: ../addressbook/libebook/e-contact.c:150 +msgid "Email 3" +msgstr "Thư điện tử 3" + +#: ../addressbook/libebook/e-contact.c:112 ../gnomecard/cardlist-headers.c:34 +msgid "Family Name" +msgstr "Họ" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:14 +msgid "File As" +msgstr "Tập tin dạng" + +#: ../addressbook/libebook/e-contact.c:111 ../gnomecard/cardlist-headers.c:32 +msgid "Given Name" +msgstr "Tên hay gọi" + +#: ../addressbook/libebook/e-contact.c:135 +msgid "Home Fax" +msgstr "Điện thư ở nhà" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:18 src/chfn.c:157 +#: src/chfn.c:159 src/chfn.c:189 +msgid "Home Phone" +msgstr "Điện thoại ở nhà" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:19 +#: ../addressbook/libebook/e-contact.c:134 +msgid "Home Phone 2" +msgstr "Điện thoại ở nhà 2" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:20 +msgid "ISDN Phone" +msgstr "Điện thoại ISDN" + +#: ../storage/exchange-hierarchy-foreign.c:255 +msgid "Journal" +msgstr "Nhật ký" + +#: ../addressbook/libebook/e-contact.c:137 +msgid "Mobile Phone" +msgstr "Điện thoại di động" + +#: ../src/search.c:155 ../ui/message.glade.h:3 src/silc-command-reply.c:274 +#: src/silc-command-reply.c:703 +msgid "Nickname" +msgstr "Tên hiệu" + +#: ../components/html-editor/template.c:88 ../sheets/UML.sheet.in.h:23 +#: todo_gui.c:2313 Expense/expense.c:1862 KeyRing/keyring.c:1689 +#: ../mimedir/mimedir-vcard.c:499 +msgid "Note" +msgstr "Ghi chú" + +#: ../desktop-directories/Office.directory.in.h:1 ../data/toc.xml.in.h:13 +msgid "Office" +msgstr "Văn phòng" + +#: ../addressbook/libebook/e-contact.c:201 ../gnomecard/cardlist-headers.c:37 +#: ../pan/message-window.c:1010 ../mimedir/mimedir-vcard.c:481 +msgid "Organization" +msgstr "Tổ chức" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:28 +#: ../addressbook/libebook/e-contact.c:139 +msgid "Other Fax" +msgstr "Điện thư khác" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:29 +#: ../addressbook/libebook/e-contact.c:138 +msgid "Other Phone" +msgstr "Điện thoại khác" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:30 +#: ../sheets/ciscotelephony.sheet.in.h:33 ../mimedir/mimedir-vcard-phone.c:196 +msgid "Pager" +msgstr "Máy nhắn tin" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:31 +#: ../addressbook/libebook/e-contact.c:141 +msgid "Primary Phone" +msgstr "Điện thoại chính" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:32 +#: ../glade/glade_menu_editor.c:1054 ../glade/glade_menu_editor.c:2414 +#: ../glade/glade_menu_editor.c:2554 ../src/glade-gtk.c:2365 +#, fuzzy +msgid "Radio" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Rađiô\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Chọn một" + +#: ../storage/exchange-permissions-dialog.c:710 ../objects/Istar/actor.c:71 +#: ../mimedir/mimedir-vcard.c:441 +msgid "Role" +msgstr "Vai trò" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:34 +msgid "Spouse" +msgstr "Vợ/Chồng" + +#. Translators: This is a vcard standard and stands for the type of +#. phone used by the hearing impaired. TTY stands for "teletype" +#. (familiar from Unix device names), and TDD is "Telecommunications +#. Device for Deaf". However, you probably want to leave this +#. abbreviation unchanged unless you know that there is actually a +#. different and established translation for this in your language. +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:41 +msgid "TTYTDD" +msgstr "TTYTDD" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:42 +#: ../addressbook/libebook/e-contact.c:143 +msgid "Telex" +msgstr "Telex" + +#: ../providers/msql/gda-msql-provider.c:533 ../src/lib/subscribe.py:178 +#: ../mimedir/mimedir-vcard-address.c:167 +msgid "Title" +msgstr "Tựa" + +#: ../libgimpwidgets/gimpunitmenu.c:660 ../plug-ins/common/postscript.c:3349 +msgid "Unit" +msgstr "Đơn vị" + +#: ../addressbook/gui/widgets/e-addressbook-view.etspec.h:45 +msgid "Web Site" +msgstr "Chỗ Mạng" + +#: ../objects/network/wanlink.c:117 +msgid "Width" +msgstr "Rộng" + +#: ../extensions/page-info/page-info-dialog.c:1299 +#: ../gncal/calendar-month-item.c:267 ../gncal/calendar-year-item.c:205 +msgid "Height" +msgstr "Cao" + +#: ../addressbook/gui/widgets/e-minicard.c:152 +msgid "Has Focus" +msgstr "Có tiêu điểm" + +#: ../libgnomedb/sel-onetable.c:203 ../libgnomedb/sel-onetarget.c:224 +#: ../glom/glom.glade.h:96 ../glom/data_structure/translatable_item.cc:234 +#: ../glom/data_structure/layout/layoutitem_field.cc:161 +#: ../glom/dialog_database_preferences.cc:49 ../glade/search.glade.h:4 +#: address_gui.c:815 address_gui.c:823 address_gui.c:840 +msgid "Field" +msgstr "Trường" + +#: ../addressbook/gui/widgets/e-minicard-label.c:143 +#: providers/msql/gda-msql-provider.c:563 +#: ../providers/msql/gda-msql-provider.c:585 +msgid "Field Name" +msgstr "Tên trường" + +#: ../addressbook/gui/widgets/e-minicard-label.c:150 +msgid "Text Model" +msgstr "Mô hình chữ" + +#: ../addressbook/gui/widgets/e-minicard-label.c:157 +msgid "Max field name length" +msgstr "Độ dài tên trường tối đa" + +#: ../addressbook/gui/widgets/e-minicard-view-widget.c:128 +msgid "Column Width" +msgstr "Độ rộng cột" + +#: ../addressbook/gui/widgets/e-minicard-view.c:172 +#: ../addressbook/gui/widgets/e-minicard-view.c:171 +msgid "" +"\n" +"\n" +"Search for the Contact\n" +"\n" +"or double-click here to create a new Contact." +msgstr "" +"\n" +"\n" +"Tìm kiếm liên lạc,\n" +"\n" +"hay nhấp đúp vào đây để tạo liên lạc mới." + +#: ../addressbook/gui/widgets/e-minicard-view.c:175 +#: ../addressbook/gui/widgets/e-minicard-view.c:174 +msgid "" +"\n" +"\n" +"There are no items to show in this view.\n" +"\n" +"Double-click here to create a new Contact." +msgstr "" +"\n" +"\n" +"Không có mục nào để xem trong khung nhìn này.\n" +"\n" +"Nhấp đúp vào đây để tạo liên lạc mới." + +#: ../addressbook/gui/widgets/e-minicard-view.c:180 +#: ../addressbook/gui/widgets/e-minicard-view.c:179 +msgid "" +"\n" +"\n" +"Search for the Contact." +msgstr "" +"\n" +"\n" +"Tìm kiếm liên lạc." + +#: ../addressbook/gui/widgets/e-minicard-view.c:182 +#: ../addressbook/gui/widgets/e-minicard-view.c:181 +msgid "" +"\n" +"\n" +"There are no items to show in this view." +msgstr "" +"\n" +"\n" +"Không có mục nào để xem trong khung nhìn này." + +#: ../addressbook/gui/widgets/e-minicard-view.c:499 +#: ../addressbook/gui/widgets/e-minicard-view.c:498 +#: libexif/olympus/mnote-olympus-tag.c:60 +msgid "Adapter" +msgstr "Bộ tiếp hợp" + +#: ../plugins/taglist/HTML.tags.xml.in.h:202 ../glade/property.c:103 +msgid "Selected" +msgstr "Đã chọn" + +#: ../addressbook/gui/widgets/e-minicard.c:168 +#: ../addressbook/gui/widgets/e-minicard.c:167 +msgid "Has Cursor" +msgstr "Có con trỏ" + +#: ../addressbook/gui/widgets/eab-contact-display.c:135 +msgid "(map)" +msgstr "(bản đồ)" + +#: ../addressbook/gui/widgets/eab-contact-display.c:145 +msgid "map" +msgstr "bản đồ" + +#: ../addressbook/gui/widgets/eab-contact-display.c:543 +msgid "List Members" +msgstr "Thành viên danh sách" + +#: ../src/personal_info.c:83 address_gui.c:1919 address_gui.c:2937 +msgid "E-mail" +msgstr "Thư điện tử" + +#: ../objects/FS/function.c:802 ../objects/Istar/actor.c:70 +#: ../widgets/gtk+.xml.in.h:144 +msgid "Position" +msgstr "Vị trí" + +#: ../addressbook/gui/widgets/eab-contact-display.c:364 +msgid "Video Conferencing" +msgstr "Hội thảo ảnh động" + +#: ../sheets/ciscotelephony.sheet.in.h:34 address_gui.c:2662 +msgid "Phone" +msgstr "Điện thoại" + +#: ../sheets/ciscotelephony.sheet.in.h:12 Expense/expense.c:551 +#: Expense/expense.c:1417 ../mimedir/mimedir-vcard-phone.c:178 +msgid "Fax" +msgstr "Điện thư" + +#: ../addressbook/gui/widgets/eab-contact-display.c:370 +#: ../mimedir/mimedir-vcard-address.c:944 ../mimedir/mimedir-vcard-email.c:566 +#: ../mimedir/mimedir-vcard-phone.c:706 +msgid "work" +msgstr "chỗ làm" + +#: ../addressbook/gui/widgets/eab-contact-display.c:377 +#: ../gnomecard/cardlist-headers.c:40 +msgid "WWW" +msgstr "WWW" + +#: ../addressbook/gui/widgets/eab-contact-display.c:597 ../blog_applet.py:39 +msgid "Blog" +msgstr "Nhật ký Mạng" + +#: ../addressbook/gui/widgets/eab-contact-display.c:385 +msgid "personal" +msgstr "cá nhân" + +#: ../gnomecard/cardlist-headers.c:38 +msgid "Job Title" +msgstr "Chức vụ" + +#: ../addressbook/gui/widgets/eab-contact-display.c:589 galeon.schemas.in.h:84 +msgid "Home page" +msgstr "Trang chủ" + +#: ogg123/cfgfile_options.c:422 +msgid "Success" +msgstr "Thành công" + +#. E_BOOK_ERROR_INVALID_ARG +#. E_BOOK_ERROR_BUSY +#: ../addressbook/gui/widgets/eab-gui-util.c:51 +msgid "Backend busy" +msgstr "Hậu phương quá bận" + +#. E_BOOK_ERROR_REPOSITORY_OFFLINE +#: ../addressbook/gui/widgets/eab-gui-util.c:52 +msgid "Repository offline" +msgstr "Kho ngoại tuyến" + +#. E_BOOK_ERROR_NO_SUCH_BOOK +#: ../addressbook/gui/widgets/eab-gui-util.c:53 +msgid "Address Book does not exist" +msgstr "Không có Sổ địa chỉ đó" + +#. E_BOOK_ERROR_NO_SELF_CONTACT +#: ../addressbook/gui/widgets/eab-gui-util.c:54 +msgid "No Self Contact defined" +msgstr "Chưa định nghĩa Tự liên lạc" + +#: gram.pl:360 ../src/gyrus-admin-acl.c:139 ../src/gyrus-admin-mailbox.c:78 +msgid "Permission denied" +msgstr "Quyền bị từ chối" + +#. E_BOOK_ERROR_CONTACT_NOT_FOUND +#: ../addressbook/gui/widgets/eab-gui-util.c:58 +msgid "Contact not found" +msgstr "Không tìm thấy liên lạc" + +#. E_BOOK_ERROR_CONTACT_ID_ALREADY_EXISTS +#: ../addressbook/gui/widgets/eab-gui-util.c:59 +msgid "Contact ID already exists" +msgstr "ID Liên lạc đã có" + +#: ../calendar/libecal/e-cal.c:5034 +msgid "Protocol not supported" +msgstr "Chưa hỗ trợ giao thức này" + +#: ../libgnomedb/gnome-db-sql-console.c:457 +msgid "Cancelled" +msgstr "Bị thôi" + +#. E_BOOK_ERROR_COULD_NOT_CANCEL +#: ../addressbook/gui/widgets/eab-gui-util.c:62 +msgid "Could not cancel" +msgstr "Không thể thôi" + +#. E_BOOK_ERROR_AUTHENTICATION_FAILED +#: ../addressbook/gui/widgets/eab-gui-util.c:63 +#: ../calendar/gui/comp-editor-factory.c:438 +msgid "Authentication Failed" +msgstr "Xác thực thất bại" + +#. E_BOOK_ERROR_AUTHENTICATION_REQUIRED +#: ../addressbook/gui/widgets/eab-gui-util.c:64 +#: ../libgnomecups/gnome-cups-ui-connection.c:628 +msgid "Authentication Required" +msgstr "Cần thiết xác thực" + +#. E_BOOK_ERROR_TLS_NOT_AVAILABLE +#: ../addressbook/gui/widgets/eab-gui-util.c:65 +msgid "TLS not Available" +msgstr "Không có TLS" + +#. E_BOOK_ERROR_CORBA_EXCEPTION +#. E_BOOK_ERROR_NO_SUCH_SOURCE +#: ../addressbook/gui/widgets/eab-gui-util.c:67 +msgid "No such source" +msgstr "Không có nguồn như vậy" + +#. E_BOOK_ERROR_OFFLINE_UNAVAILABLE +#: ../addressbook/gui/widgets/eab-gui-util.c:68 +msgid "Not available in offline mode" +msgstr "Không sẵn sàng trong chế độ ngoại tuyến" + +#. E_BOOK_ERROR_OTHER_ERROR +#: ../addressbook/gui/widgets/eab-gui-util.c:69 +msgid "Other error" +msgstr "Lỗi khác" + +#. E_BOOK_ERROR_INVALID_SERVER_VERSION +#: ../addressbook/gui/widgets/eab-gui-util.c:70 +msgid "Invalid server version" +msgstr "Phiên bản máy phục vụ không hợp lệ" + +#: ../addressbook/gui/widgets/eab-gui-util.c:93 +msgid "" +"We were unable to open this addressbook. This either means this book is not " +"marked for offline usage or not yet downloaded for offline usage. Please " +"load the addressbook once in online mode to download its contents" +msgstr "" +"Chưa có mở được sổ địa chỉ này. Hoặc vì sổ này không có dấu cho phép sử dụng " +"khi ngoại tuyến, hoặc chưa tải nó về để sử dụng ngoại tuyến. Hãy tải sổ địa " +"chỉ đó một lần trong chế độ trực tuyến, để tải nội dung nó về." + +#: ../addressbook/gui/widgets/eab-gui-util.c:102 +#, c-format +msgid "" +"We were unable to open this addressbook. Please check that the path %s " +"exists and that you have permission to access it." +msgstr "" +"Không thể mở sổ địa chỉ này. Vui lòng kiểm tra lại có đường dẫn « %s » và " +"bạn có quyền truy cập vào nó." + +#: ../addressbook/gui/widgets/eab-gui-util.c:111 +#: ../addressbook/gui/widgets/eab-gui-util.c:110 +msgid "" +"We were unable to open this addressbook. This either means you have entered " +"an incorrect URI, or the LDAP server is unreachable." +msgstr "" +"Không thể mở sổ địa chỉ này. Nguyên nhân hoặc là do bạn đã gõ sai địa chỉ " +"Mạng, hoặc là do máy phục vụ LDAP không thể truy cập." + +#: ../addressbook/gui/widgets/eab-gui-util.c:116 +#: ../addressbook/gui/widgets/eab-gui-util.c:115 +msgid "" +"This version of Evolution does not have LDAP support compiled in to it. If " +"you want to use LDAP in Evolution, you must install an LDAP-enabled " +"Evolution package." +msgstr "" +"Phiên bản Evolution này không được biên dịch để hỗ trợ LDAP. Nếu bạn muốn " +"dùng LDAP trong Evolution, bạn phải cài đặt gói Evolution hỗ trợ LDAP." + +#: ../addressbook/gui/widgets/eab-gui-util.c:123 +#: ../addressbook/gui/widgets/eab-gui-util.c:122 +msgid "" +"We were unable to open this addressbook. This either means you have entered " +"an incorrect URI, or the server is unreachable." +msgstr "" +"Không thể mở sổ địa chỉ này. Nguyên nhân hoặc là do bạn đã gõ sai địa chỉ " +"Mạng đó, hoặc là do máy phục vụ không thể truy cập." + +#: ../addressbook/gui/widgets/eab-gui-util.c:146 +msgid "" +"More cards matched this query than either the server is \n" +"configured to return or Evolution is configured to display.\n" +"Please make your search more specific or raise the result limit in\n" +"the directory server preferences for this addressbook." +msgstr "" +"Quá nhiều thẻ khớp với truy vấn này, nhiều hơn cấu hình\n" +"của máy phục vụ có thể trả gởi, hoặc cấu hình của Evolution\n" +"có thể hiển thị. Bạn hãy tìm kiếm chính xác hơn hoặc tăng giới hạn\n" +"kết quả trong Tùy thích máy phục vụ thư mục cho sổ địa chỉ này." + +#: ../addressbook/gui/widgets/eab-gui-util.c:152 +msgid "" +"The time to execute this query exceeded the server limit or the limit\n" +"you have configured for this addressbook. Please make your search\n" +"more specific or raise the time limit in the directory server\n" +"preferences for this addressbook." +msgstr "" +"Thời gian thực hiện truy vấn này vượt quá giới hạn máy phục vụ\n" +"hoặc giới hạn do bạn cấu hình cho sổ địa chỉ này.\n" +"Vui lòng tìm kiếm chính xác hơn hoặc tăng giới hạn thời gian trong\n" +"Tùy thích máy phục vụ thư mục cho sổ địa chỉ này." + +#: ../addressbook/gui/widgets/eab-gui-util.c:158 +#: ../addressbook/gui/widgets/eab-gui-util.c:157 +msgid "The backend for this addressbook was unable to parse this query." +msgstr "Hậu phương cho sổ địa chỉ này không thể phân tách truy vấn này." + +#: ../addressbook/gui/widgets/eab-gui-util.c:161 +#: ../addressbook/gui/widgets/eab-gui-util.c:160 +msgid "The backend for this addressbook refused to perform this query." +msgstr "Hậu phương cho sổ địa chỉ này từ chối thực hiện truy vấn này." + +#: ../addressbook/gui/widgets/eab-gui-util.c:164 +#: ../addressbook/gui/widgets/eab-gui-util.c:163 +msgid "This query did not complete successfully." +msgstr "Truy vấn không hoàn tất." + +#: ../addressbook/gui/widgets/eab-gui-util.c:186 +#: ../addressbook/gui/widgets/eab-gui-util.c:185 +msgid "Error adding list" +msgstr "Gặp lỗi khi thêm danh sách" + +#: ../addressbook/gui/widgets/eab-gui-util.c:687 +msgid "Error adding contact" +msgstr "Gặp lỗi khi thêm liên lạc" + +#: ../addressbook/gui/widgets/eab-gui-util.c:197 +#: ../addressbook/gui/widgets/eab-gui-util.c:196 +msgid "Error modifying list" +msgstr "Gặp lỗi khi sửa đổi danh sách" + +#: ../addressbook/gui/widgets/eab-gui-util.c:197 +#: ../addressbook/gui/widgets/eab-gui-util.c:196 +msgid "Error modifying contact" +msgstr "Gặp lỗi khi sửa đổi liên lạc" + +#: ../addressbook/gui/widgets/eab-gui-util.c:209 +#: ../addressbook/gui/widgets/eab-gui-util.c:208 +msgid "Error removing list" +msgstr "Gặp lỗi khi gỡ bỏ danh sách" + +#: ../addressbook/gui/widgets/eab-gui-util.c:642 +msgid "Error removing contact" +msgstr "Gặp lỗi khi gỡ bỏ liên lạc" + +#: ../addressbook/gui/widgets/eab-gui-util.c:291 +#: ../addressbook/gui/widgets/eab-gui-util.c:290 +#, c-format +msgid "" +"Opening %d contact will open %d new window as well.\n" +"Do you really want to display this contact?" +msgid_plural "" +"Opening %d contact will open %d new window as well.\n" +"Do you really want to display this contact?" +msgstr[0] "" +"Việc mở %d liên lạc sẽ mở %d cửa sổ mới cùng lúc.\n" +"Bạn có thật sự muốn hiển thị liên lạc này không?" + +#: ../addressbook/gui/widgets/eab-gui-util.c:320 +#: ../addressbook/gui/widgets/eab-gui-util.c:319 +#, c-format +msgid "" +"%s already exists\n" +"Do you want to overwrite it?" +msgstr "" +"%s đã có.\n" +"Bạn có muốn ghi đè lên nó không?" + +#: ../addressbook/gui/widgets/eab-gui-util.c:324 ../src/actions.c:492 +#: ../src/actions.c:808 ../src/ui-gui.cc:244 +msgid "Overwrite" +msgstr "Ghi đè" + +#: ../addressbook/gui/widgets/eab-gui-util.c:371 +msgid "contact" +msgid_plural "contact" +msgstr[0] "liên lạc" + +#: ../addressbook/gui/widgets/eab-gui-util.c:418 +msgid "card.vcf" +msgstr "card.vcf" + +#: ../addressbook/gui/widgets/eab-gui-util.c:755 +#: ../addressbook/gui/widgets/eab-gui-util.c:748 +msgid "Move contact to" +msgstr "Chuyển liên lạc tới" + +#: ../addressbook/gui/widgets/eab-gui-util.c:757 +#: ../addressbook/gui/widgets/eab-gui-util.c:750 +msgid "Copy contact to" +msgstr "Chép liên lạc tới" + +#: ../addressbook/gui/widgets/eab-gui-util.c:760 +#: ../addressbook/gui/widgets/eab-gui-util.c:753 +msgid "Move contacts to" +msgstr "Chuyển các liên lạc tới" + +#: ../addressbook/gui/widgets/eab-gui-util.c:762 +#: ../addressbook/gui/widgets/eab-gui-util.c:755 +msgid "Copy contacts to" +msgstr "Chép các liên lạc tới" + +#: ../addressbook/gui/widgets/eab-gui-util.c:765 +#: ../addressbook/gui/widgets/eab-gui-util.c:758 +msgid "Select target addressbook." +msgstr "Chọn sổ địa chỉ đích." + +#: ../addressbook/gui/widgets/eab-gui-util.c:988 +#: ../addressbook/gui/widgets/eab-gui-util.c:982 +msgid "Multiple VCards" +msgstr "Nhiều VCard" + +#: ../addressbook/gui/widgets/eab-gui-util.c:991 +#: ../addressbook/gui/widgets/eab-gui-util.c:985 +#, c-format +msgid "VCard for %s" +msgstr "VCard cho « %s »" + +#: ../addressbook/gui/widgets/eab-gui-util.c:1032 +#: ../addressbook/gui/widgets/eab-gui-util.c:1050 +#: ../mimedir/mimedir-vcomponent.c:387 +msgid "Contact information" +msgstr "Thông tin lien lạc" + +#: ../addressbook/gui/widgets/eab-gui-util.c:1052 +#, c-format +msgid "Contact information for %s" +msgstr "Thông tin lien lạc cho %s" + +#: ../addressbook/gui/widgets/eab-popup-control.c:431 +msgid "Primary Email" +msgstr "Thư điện từ chính" + +#: ../addressbook/gui/widgets/eab-popup-control.c:567 +msgid "Select an Action" +msgstr "Chọn hành động" + +#: ../addressbook/gui/widgets/eab-popup-control.c:575 +#, c-format +msgid "Create a new contact \"%s\"" +msgstr "Tạo liên lạc mới « %s »" + +#: ../addressbook/gui/widgets/eab-popup-control.c:591 +#, c-format +msgid "Add address to existing contact \"%s\"" +msgstr "Thêm địa chỉ vào liên lạc đã có « %s »" + +#: ../addressbook/gui/widgets/eab-popup-control.c:869 +msgid "Querying Address Book..." +msgstr "Đang truy vấn Sổ địa chỉ..." + +#: ../addressbook/gui/widgets/eab-popup-control.c:968 +#: ../addressbook/gui/widgets/eab-popup-control.c:970 +msgid "Merge E-Mail Address" +msgstr "Trộn địa chỉ thư điện tử" + +#: ../addressbook/gui/widgets/eab-vcard-control.c:139 +#, c-format +msgid "There is one other contact." +msgid_plural "There are %d other contacts." +msgstr[0] "Có %d liên lạc khác." + +#: ../addressbook/gui/widgets/eab-vcard-control.c:223 +#: ../addressbook/gui/widgets/eab-vcard-control.c:272 +msgid "Show Full VCard" +msgstr "Hiện toàn vCard" + +#: ../addressbook/gui/widgets/eab-vcard-control.c:227 +msgid "Show Compact VCard" +msgstr "Hiện vCard tóm gọn" + +#: ../addressbook/gui/widgets/eab-vcard-control.c:277 +msgid "Save in addressbook" +msgstr "Lưu vào sổ địa chỉ" + +#: ../addressbook/gui/widgets/gal-view-factory-minicard.c:25 +msgid "Card View" +msgstr "Khung xem thẻ" + +#: ../addressbook/gui/widgets/gal-view-factory-treeview.c:26 +msgid "GTK Tree View" +msgstr "Khung xem Cây GTK" + +#: ../calendar/importers/icalendar-importer.c:643 +msgid "Importing ..." +msgstr "Đang nhập..." + +#: ../addressbook/importers/evolution-ldif-importer.c:761 +#: ../addressbook/importers/evolution-ldif-importer.c:652 +msgid "LDAP Data Interchange Format (.ldif)" +msgstr "Dạng thức chuyển đổi lẫn nhau dữ liệu LDAP (.ldif)" + +#: ../addressbook/importers/evolution-ldif-importer.c:762 +#: ../addressbook/importers/evolution-ldif-importer.c:653 +msgid "Evolution LDIF importer" +msgstr "Bộ nhập LDIF Evolution" + +# Name: do not translate/ tên: đừng dịch +#: ../addressbook/importers/evolution-vcard-importer.c:554 +#: ../addressbook/importers/evolution-vcard-importer.c:529 +msgid "VCard (.vcf, .gcrd)" +msgstr "vCard (.vcf, .gcrd)" + +#: ../addressbook/importers/evolution-vcard-importer.c:555 +#: ../addressbook/importers/evolution-vcard-importer.c:530 +msgid "Evolution VCard Importer" +msgstr "Bộ nhập vCard Evolution" + +#: ../addressbook/printing/e-contact-print-envelope.c:213 +#: ../addressbook/printing/e-contact-print-envelope.c:234 +msgid "Print envelope" +msgstr "In phong bì" + +#: ../addressbook/printing/e-contact-print.c:1033 +#: ../addressbook/printing/e-contact-print.c:1001 +msgid "Print contacts" +msgstr "In các liên lạc" + +#: ../addressbook/printing/e-contact-print.c:1093 +msgid "Print contact" +msgstr "In liên lạc" + +#: ../addressbook/printing/e-contact-print.glade.h:1 +msgid "10 pt. Tahoma" +msgstr "10 pt. Tahoma" + +#: ../addressbook/printing/e-contact-print.glade.h:2 +msgid "8 pt. Tahoma" +msgstr "8 pt. Tahoma" + +#: ../addressbook/printing/e-contact-print.glade.h:3 +msgid "Blank forms at end:" +msgstr "Mẫu trống tại cuối:" + +#: ../plug-ins/print/gimp_main_window.c:575 ../app/diapagelayout.c:212 +msgid "Bottom:" +msgstr "Dưới:" + +#: ../plug-ins/print/gimp_main_window.c:1006 +msgid "Dimensions:" +msgstr "Các chiều : " + +#: ../addressbook/printing/e-contact-print.glade.h:7 +msgid "F_ont..." +msgstr "_Phông chữ..." + +#: ../addressbook/printing/e-contact-print.glade.h:9 +msgid "Footer:" +msgstr "Chân trang:" + +#: web/template/resources_edit_main.tpl:16 +msgid "Format" +msgstr "Dạng thức" + +#: ../widgets/table/e-table-selection-model.c:318 ../src/menus.c:280 +#: ../src/orca/rolenames.py:498 +msgid "Header" +msgstr "Đầu trang" + +#: ../addressbook/printing/e-contact-print.glade.h:12 +msgid "Header/Footer" +msgstr "Đầu/Chân trang" + +#: ../addressbook/printing/e-contact-print.glade.h:13 +msgid "Headings" +msgstr "Tiêu đề" + +#: ../addressbook/printing/e-contact-print.glade.h:14 +msgid "Headings for each letter" +msgstr "Tiêu đề cho mỗi lá thư" + +#: ../glade/property.c:816 +msgid "Height:" +msgstr "Cao :" + +#: ../addressbook/printing/e-contact-print.glade.h:16 +msgid "Immediately follow each other" +msgstr "Theo ngay sau mỗi cái" + +#: ../addressbook/printing/e-contact-print.glade.h:17 +msgid "Include:" +msgstr "Gồm:" + +#: ../app/widgets/widgets-enums.c:54 ../plug-ins/print/gimp_main_window.c:494 +#: libexif/exif-entry.c:406 libexif/exif-entry.c:483 +msgid "Landscape" +msgstr "Nằm ngang" + +#: ../plug-ins/print/gimp_main_window.c:521 ../app/diapagelayout.c:225 +msgid "Left:" +msgstr "Trái:" + +#: ../addressbook/printing/e-contact-print.glade.h:20 +msgid "Letter tabs on side" +msgstr "Tab thư tại bên" + +#: ../app/diapagelayout.c:187 +msgid "Margins" +msgstr "Viền" + +#: ../glade/gbwidgets/gbhbuttonbox.c:132 ../src/form-editor/table-prop.cc:307 +msgid "Number of columns:" +msgstr "Số cột:" + +#: ../gtk/gtknotebook.c:405 ../src/orca/rolenames.py:328 +#, c-format +msgid "Page" +msgstr "Trang" + +#: ../addressbook/printing/e-contact-print.glade.h:26 +msgid "Page Setup:" +msgstr "Thiết lập trang:" + +#: ../gnome-cups-manager/gnome-cups-manager.glade.h:9 +msgid "Paper" +msgstr "Giấy" + +#: ../addressbook/printing/e-contact-print.glade.h:28 +msgid "Paper source:" +msgstr "Nguồn giấy:" + +#: ../plug-ins/print/gimp_main_window.c:493 ../app/preferences.c:135 +#: libexif/canon/mnote-canon-entry.c:104 libexif/exif-entry.c:406 +#: libexif/exif-entry.c:481 +msgid "Portrait" +msgstr "Thẳng đứng" + +#: ../addressbook/printing/e-contact-print.glade.h:30 +#: ../app/tools/gimptransformoptions.c:366 +#: ../glade/gnome/gnomepixmapentry.c:75 +msgid "Preview:" +msgstr "Xem thử :" + +#: ../addressbook/printing/e-contact-print.glade.h:31 +msgid "Print using gray shading" +msgstr "In bóng xám" + +#: ../addressbook/printing/e-contact-print.glade.h:32 +msgid "Reverse on even pages" +msgstr "Để nguyên trang chẵn" + +#: ../addressbook/printing/e-contact-print.glade.h:33 +#: ../app/diapagelayout.c:238 ../directed.xml.in.h:12 ../gok.glade2.h:105 +#: ../plug-ins/MapObject/mapobject_ui.c:1111 +#: ../plug-ins/print/gimp_main_window.c:547 +msgid "Right:" +msgstr "Phải:" + +#: ../addressbook/printing/e-contact-print.glade.h:34 +msgid "Sections:" +msgstr "Phần:" + +#: ../addressbook/printing/e-contact-print.glade.h:35 +msgid "Shading" +msgstr "Bóng" + +#: ../src/filexferdlg.c:99 ../src/filexferdlg.c:182 ../src/gtkfunc.c:261 +msgid "Size:" +msgstr "Cỡ :" + +#: ../addressbook/printing/e-contact-print.glade.h:37 +msgid "Start on a new page" +msgstr "Bắt đầu trang mới" + +#: ../addressbook/printing/e-contact-print.glade.h:38 +msgid "Style name:" +msgstr "Tên kiểu dáng:" + +#: ../plug-ins/print/gimp_main_window.c:534 ../app/diapagelayout.c:199 +msgid "Top:" +msgstr "Trên:" + +#: ../plug-ins/print/gimp_main_window.c:1216 ../app/preferences.c:144 +#: ../glade/property.c:813 +msgid "Width:" +msgstr "Rộng:" + +#: ../addressbook/printing/e-contact-print.glade.h:42 +msgid "_Font..." +msgstr "_Phông chữ..." + +#: ../addressbook/printing/test-contact-print-style-editor.c:53 +msgid "Contact Print Style Editor Test" +msgstr "Thử trình sửa đổi kiểu dáng in liên lạc" + +#: ../addressbook/printing/test-print.c:53 +msgid "Copyright (C) 2000, Ximian, Inc." +msgstr "Bản quyền © năm 2000, Ximian, Inc." + +#: ../addressbook/printing/test-contact-print-style-editor.c:56 +msgid "This should test the contact print style editor widget" +msgstr "Hành động này nên thử ra ô điều khiển sửa đổi kiểu dáng in liên lạc." + +#: ../addressbook/printing/test-print.c:52 +msgid "Contact Print Test" +msgstr "Kiểm thử In liên lạc" + +#: ../addressbook/printing/test-print.c:55 +msgid "This should test the contact print code" +msgstr "Hành động này nên thử ra mã nguồn in liên lạc." + +#: ../addressbook/tools/evolution-addressbook-export-list-folders.c:49 +msgid "Can not open file" +msgstr "Không thể mở tập tin" + +#: ../addressbook/tools/evolution-addressbook-export-list-folders.c:44 +#: ../addressbook/tools/evolution-addressbook-export-list-folders.c:43 +msgid "Couldn't get list of addressbooks" +msgstr "Không thể lấy danh sách các sổ địa chỉ" + +#: ../addressbook/tools/evolution-addressbook-export-list-folders.c:72 +#: ../addressbook/tools/evolution-addressbook-export-list-folders.c:71 +msgid "failed to open book" +msgstr "lỗi mở sổ" + +#: ../addressbook/tools/evolution-addressbook-export.c:56 +msgid "Specify the output file instead of standard output" +msgstr "Ghi rõ tập tin xuất thay vào thiết bị xuất chuẩn" + +#: ../addressbook/tools/evolution-addressbook-export.c:57 +msgid "OUTPUTFILE" +msgstr "TẬP_TIN_XUẤT" + +#: ../addressbook/tools/evolution-addressbook-export.c:58 +msgid "List local addressbook folders" +msgstr "Liệt kê các thư mục sổ địa chỉ địa phương" + +#: ../addressbook/tools/evolution-addressbook-export.c:60 +msgid "Show cards as vcard or csv file" +msgstr "" +"Hiển thị mọi thẻ dạng vCard (thẻ ảo) hoặc csv (định giới bằng dấu phẩy)" + +# Format name: do not translate/ tên dạng thức: đừng dịch +#: ../addressbook/tools/evolution-addressbook-export.c:60 +msgid "[vcard|csv]" +msgstr "[vcard|csv]" + +#: ../addressbook/tools/evolution-addressbook-export.c:61 +msgid "Export in asynchronous mode" +msgstr "Xuất theo chế độ không đồng bộ " + +#: ../addressbook/tools/evolution-addressbook-export.c:63 +msgid "" +"The number of cards in one output file in asychronous mode, default size 100." +msgstr "" +"Tổng số thẻ trong một tập tin kết xuất riêng lẻ trong chế độ không đồng bộ : " +"kích cỡ mặc định là 100." + +#: ../addressbook/tools/evolution-addressbook-export.c:91 +msgid "" +"Command line arguments error, please use --help option to see the usage." +msgstr "" +"Lỗi đối số dòng lệnh, hãy dùng tùy chọn « --help » (trợ giúp) để xem cách sử " +"dụng đúng." + +#: ../addressbook/tools/evolution-addressbook-export.c:105 +msgid "Only support csv or vcard format." +msgstr "Chỉ hỗ trợ dạng thức csv hoặc vCard (thẻ ảo)." + +#: ../addressbook/tools/evolution-addressbook-export.c:114 +msgid "In async mode, output must be file." +msgstr "Trong chế độ không đồng bộ, kết xuất phải là tập tin." + +#: ../addressbook/tools/evolution-addressbook-export.c:122 +msgid "In normal mode, there is no need for the size option." +msgstr "Trong chế độ thường, không cần tùy chọn về kích thước." + +#: ../addressbook/tools/evolution-addressbook-export.c:153 +msgid "Unhandled error" +msgstr "Không biết lỗi đó" + +#: ../addressbook/tools/evolution-addressbook-import.c:46 +msgid "Error loading default addressbook." +msgstr "Gặp lỗi khi tải sổ địa chỉ mặc định." + +#: ../addressbook/tools/evolution-addressbook-import.c:67 +msgid "Input File" +msgstr "Tập tin nhập" + +#: ../addressbook/tools/evolution-addressbook-import.c:82 +msgid "No filename provided." +msgstr "Chưa cung cấp tên tập tin." + +#: ../calendar/calendar.error.xml.h:1 +msgid "" +"Adding a meaningful summary to your appointment will give your recipients an " +"idea of what your appointment is about." +msgstr "" +"Việc thêm một Tóm tắt có nghĩa vào cuộc hẹn bạn sẽ cho người nhận biết ý " +"kiến về lý do của cuộc hẹn này." + +#: ../calendar/calendar.error.xml.h:2 +msgid "" +"Adding a meaningful summary to your task will give your recipients an idea " +"of what your task is about." +msgstr "" +"Việc thêm một Tóm tắt có nghĩa vào tác vụ bạn sẽ cho người nhận biết ý kiến " +"về lý do của tác vụ này." + +#: ../calendar/calendar.error.xml.h:3 ../calendar/calendar.error.xml.h:5 +msgid "" +"All information in these journal entries will be deleted and can not be " +"restored." +msgstr "Mọi thông tin của những mục nhật ký này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:4 ../calendar/calendar.error.xml.h:6 +msgid "" +"All information in this journal will be deleted and can not be restored." +msgstr "Mọi thông tin của nhật ký này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:5 ../calendar/calendar.error.xml.h:7 +msgid "" +"All information on these appointments will be deleted and can not be " +"restored." +msgstr "Mọi thông tin của những cuộc hẹn này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:6 ../calendar/calendar.error.xml.h:8 +msgid "All information on these tasks will be deleted and can not be restored." +msgstr "Mọi thông tin về những tác vụ này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:7 ../calendar/calendar.error.xml.h:9 +msgid "" +"All information on this appointment will be deleted and can not be restored." +msgstr "Mọi thông tin của cuộc hẹn này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:8 ../calendar/calendar.error.xml.h:10 +msgid "" +"All information on this journal entry will be deleted and can not be " +"restored." +msgstr "Mọi thông tin của mục nhật ký này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:9 ../calendar/calendar.error.xml.h:11 +msgid "" +"All information on this meeting will be deleted and can not be restored." +msgstr "Mọi thông tin của cuộc họp này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:10 ../calendar/calendar.error.xml.h:12 +msgid "All information on this task will be deleted and can not be restored." +msgstr "Mọi thông tin về tác vụ này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:11 ../calendar/calendar.error.xml.h:13 +msgid "Are you sure you want to delete the '{0}' task?" +msgstr "Bạn có chắc muốn xoá bỏ tác vụ « {0} » không?" + +#: ../calendar/calendar.error.xml.h:12 ../calendar/calendar.error.xml.h:14 +msgid "Are you sure you want to delete the appointment titled '{0}'?" +msgstr "Bạn có chắc muốn xoá bỏ cuộc hẹn tên « {0} » không?" + +#: ../calendar/calendar.error.xml.h:13 ../calendar/calendar.error.xml.h:15 +msgid "Are you sure you want to delete the journal entry '{0}'?" +msgstr "Bạn có chắc muốn xoá bỏ mục nhật ký « {0} » không?" + +#: ../calendar/calendar.error.xml.h:14 ../calendar/calendar.error.xml.h:16 +msgid "Are you sure you want to delete these {0} appointments?" +msgstr "Bạn có chắc muốn xoá bỏ những {0} cuộc hẹn này không?" + +#: ../calendar/calendar.error.xml.h:15 ../calendar/calendar.error.xml.h:17 +msgid "Are you sure you want to delete these {0} journal entries?" +msgstr "Bạn có chắc muốn xoá bỏ những {0} mục nhật ký này không?" + +#: ../calendar/calendar.error.xml.h:16 ../calendar/calendar.error.xml.h:18 +msgid "Are you sure you want to delete these {0} tasks?" +msgstr "Bạn có chắc muốn xoá bỏ những {0} tác vụ này không?" + +#: ../calendar/calendar.error.xml.h:17 ../calendar/calendar.error.xml.h:19 +msgid "Are you sure you want to delete this appointment?" +msgstr "Bạn có chắc muốn xoá bỏ cuộc hẹn này không?" + +#: ../calendar/calendar.error.xml.h:18 ../calendar/calendar.error.xml.h:20 +msgid "Are you sure you want to delete this journal entry?" +msgstr "Bạn có chắc muốn xoá bỏ mục nhật ký này không?" + +#: ../calendar/calendar.error.xml.h:19 ../calendar/calendar.error.xml.h:21 +msgid "Are you sure you want to delete this meeting?" +msgstr "Bạn có chắc muốn xoá bỏ cuộc họp này không?" + +#: ../calendar/calendar.error.xml.h:20 ../calendar/calendar.error.xml.h:22 +msgid "Are you sure you want to delete this task?" +msgstr "Bạn có chắc muốn xoá bỏ tác vụ này không?" + +#: ../calendar/calendar.error.xml.h:21 ../calendar/calendar.error.xml.h:23 +msgid "Are you sure you want to send the appointment without a summary?" +msgstr "Bạn có chắc muốn gởi thư không có tóm tắt không? (Không đệ nghị.)" + +#: ../calendar/calendar.error.xml.h:22 ../calendar/calendar.error.xml.h:24 +msgid "Are you sure you want to send the task without a summary?" +msgstr "Bạn có chắc muốn gởi tác vụ không có tóm tắt không?" + +#: ../calendar/calendar.error.xml.h:23 ../calendar/calendar.error.xml.h:25 +msgid "Delete calendar '{0}'?" +msgstr "Xoá bỏ lịch « {0} » không?" + +#: ../calendar/calendar.error.xml.h:24 +msgid "Delete memo list '{0}'?" +msgstr "Xoá bỏ danh sách ghi nhớ « {0} » không?" + +#: ../calendar/calendar.error.xml.h:25 ../calendar/calendar.error.xml.h:26 +msgid "Delete task list '{0}'?" +msgstr "Xoá bỏ danh sách tác vụ « {0} » không?" + +#: ../calendar/calendar.error.xml.h:26 ../calendar/calendar.error.xml.h:28 +msgid "Don't Send" +msgstr "Không gởi" + +#: ../calendar/calendar.error.xml.h:27 ../calendar/calendar.error.xml.h:29 +msgid "Download in progress. Do you want to save the appointment?" +msgstr "Đang tải về. Bạn có muốn lưu cuộc hẹn không?" + +#: ../calendar/calendar.error.xml.h:28 ../calendar/calendar.error.xml.h:30 +msgid "Download in progress. Do you want to save the task?" +msgstr "Đang tải về. Bạn có muốn lưu tác vụ không?" + +#: ../calendar/calendar.error.xml.h:29 ../calendar/calendar.error.xml.h:31 +msgid "Editor could not be loaded." +msgstr "Không thể tải trình hiệu chỉnh." + +#: ../calendar/calendar.error.xml.h:30 ../calendar/calendar.error.xml.h:32 +msgid "" +"Email invitations will be sent to all participants and allow them to RSVP." +msgstr "" +"Lời mời thư điện tử sẽ được gởi cho mọi người dự và cho phép họ trả lời " +"trước." + +#: ../calendar/calendar.error.xml.h:31 ../calendar/calendar.error.xml.h:33 +msgid "" +"Email invitations will be sent to all participants and allow them to accept " +"this task." +msgstr "" +"Lời mời thư điện từ sẽ được gởi cho mọi người dự và cho phép họ chấp nhận " +"tác vụ này." + +#: ../calendar/calendar.error.xml.h:32 ../calendar/calendar.error.xml.h:34 +msgid "Error loading calendar" +msgstr "Gặp lỗi khi tải lịch" + +#: ../calendar/calendar.error.xml.h:33 +msgid "Error loading memo list" +msgstr "Gặp lỗi khi tải danh sách ghi nhớ" + +#: ../calendar/calendar.error.xml.h:34 ../calendar/calendar.error.xml.h:35 +msgid "Error loading task list" +msgstr "Gặp lỗi khi tải danh sách tác vụ" + +#: ../calendar/calendar.error.xml.h:35 ../calendar/calendar.error.xml.h:36 +msgid "" +"If you don't send a cancellation notice, the other participants may not know " +"the journal has been deleted." +msgstr "" +"Nếu bạn không gởi thông báo hủy bỏ, những người dự khác có thể sẽ không biết " +"nhật ký đã được xoá bỏ." + +#: ../calendar/calendar.error.xml.h:36 ../calendar/calendar.error.xml.h:37 +msgid "" +"If you don't send a cancellation notice, the other participants may not know " +"the meeting is canceled." +msgstr "" +"Nếu bạn không gởi thông báo hủy bỏ, những người dự khác có thể sẽ không biết " +"cuộc họp đã bị hủy bỏ." + +#: ../calendar/calendar.error.xml.h:37 ../calendar/calendar.error.xml.h:38 +msgid "" +"If you don't send a cancellation notice, the other participants may not know " +"the task has been deleted." +msgstr "" +"Nếu bạn không gởi thông báo hủy bỏ, những người dự khác có thể sẽ không biết " +"tác vụ đã được xoá bỏ." + +#: ../calendar/calendar.error.xml.h:39 ../calendar/calendar.error.xml.h:41 +msgid "Send Notice" +msgstr "Gởi thông báo" + +#: ../calendar/calendar.error.xml.h:40 ../calendar/calendar.error.xml.h:42 +msgid "" +"Sending updated information allows other participants to keep their " +"calendars up to date." +msgstr "" +"Gởi thông tin cập nhật cho phép những người dự khác cập nhật lại lịch của họ." + +#: ../calendar/calendar.error.xml.h:41 ../calendar/calendar.error.xml.h:43 +msgid "" +"Sending updated information allows other participants to keep their task " +"lists up to date." +msgstr "" +"Việc gởi thông tin cập nhật cho phép những người dự khác cập nhật danh sách " +"tác vụ của họ." + +#: ../calendar/calendar.error.xml.h:42 +msgid "" +"Some attachments are being downloaded. Saving the appointment would result " +"in the loss of these attachments." +msgstr "" +"Hiện thời đang tải về một số đính kèm. Khi lưu cuộc hẹn này, sẽ cũng mất các " +"đính kèm này." + +#: ../calendar/calendar.error.xml.h:43 +msgid "" +"Some attachments are being downloaded. Saving the task would result in the " +"loss of these attachments." +msgstr "" +"Hiện thời đang tải về một số đính kèm. Khi lưu tác vụ này, sẽ cũng mất các " +"đính kèm này." + +#: ../calendar/calendar.error.xml.h:44 +msgid "Some features may not work properly with your current server." +msgstr "" +"Có lẽ một số tính năng sẽ không hoạt động với máy phục vụ hiện thời của bạn." + +#: ../calendar/calendar.error.xml.h:45 +msgid "The Evolution calendar has quit unexpectedly." +msgstr "Lịch Evolution đã thoát bất ngờ." + +#: ../calendar/calendar.error.xml.h:46 +msgid "The Evolution tasks have quit unexpectedly." +msgstr "Tác vụ Evolution đã thoát bất ngờ." + +#: ../calendar/calendar.error.xml.h:47 +msgid "The calendar is not marked for offline usage." +msgstr "Chưa đánh dấu lịch này để sử dụng khi ngoại tuyến." + +#: ../calendar/calendar.error.xml.h:48 +msgid "The memo list is not marked for offline usage" +msgstr "Chưa đánh dấu danh sách ghi nhớ này để sử dụng khi ngoại tuyến." + +#: ../calendar/calendar.error.xml.h:49 +msgid "The task list is not marked for offline usage." +msgstr "Chưa đánh dấu danh sách tác vụ này để sử dụng khi ngoại tuyến." + +#: ../calendar/calendar.error.xml.h:50 ../calendar/calendar.error.xml.h:49 +msgid "This calendar will be removed permanently." +msgstr "Lịch này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:51 +msgid "This memo list will be removed permanently." +msgstr "Danh sách ghi nhớ này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:52 ../calendar/calendar.error.xml.h:50 +msgid "This task list will be removed permanently." +msgstr "Tác vụ này sẽ bị xoá bỏ hoàn toàn." + +#: ../calendar/calendar.error.xml.h:53 ../calendar/calendar.error.xml.h:51 +msgid "Would you like to save your changes to this appointment?" +msgstr "Bạn có muốn lưu các thay đổi của cuộc hẹn này không?" + +#: ../calendar/calendar.error.xml.h:54 ../calendar/calendar.error.xml.h:52 +msgid "Would you like to save your changes to this task?" +msgstr "Bạn có muốn lưu các thay đổi của tác vụ này không?" + +#: ../calendar/calendar.error.xml.h:55 ../calendar/calendar.error.xml.h:53 +msgid "Would you like to send a cancellation notice for this journal entry?" +msgstr "Bạn có muốn gởi thông báo hủy bỏ cho mục nhật ký này không?" + +#: ../calendar/calendar.error.xml.h:56 ../calendar/calendar.error.xml.h:54 +msgid "Would you like to send all the participants a cancellation notice?" +msgstr "Bạn có muốn gởi cho mọi người tham gia thông báo hủy bỏ không?" + +#: ../calendar/calendar.error.xml.h:57 ../calendar/calendar.error.xml.h:55 +msgid "Would you like to send meeting invitations to participants?" +msgstr "Bạn có muốn gởi lời mời họp đến những người dự không?" + +#: ../calendar/calendar.error.xml.h:58 ../calendar/calendar.error.xml.h:56 +msgid "Would you like to send this task to participants?" +msgstr "Bạn có muốn gởi tác vụ này cho những người dự không?" + +#: ../calendar/calendar.error.xml.h:59 ../calendar/calendar.error.xml.h:57 +msgid "Would you like to send updated meeting information to participants?" +msgstr "" +"Bạn có muốn gởi thông tin cuộc họp đã cập nhật cho những người dự không?" + +#: ../calendar/calendar.error.xml.h:60 ../calendar/calendar.error.xml.h:58 +msgid "Would you like to send updated task information to participants?" +msgstr "Bạn có muốn gởi thông tin tác vụ đã cập nhật cho những người dự không?" + +#: ../calendar/calendar.error.xml.h:61 +msgid "" +"You are connecting to an unsupported GroupWise server and may encounter " +"problems using Evolution. For best results, the server should be upgraded to " +"a supported version." +msgstr "" +"Bạn đang kết nối đến một máy phục vụ Groupwise không được hỗ trợ thì có lẽ " +"sẽ gặp khó khăn sử dụng trình Evolution. Để được kết quả tốt nhất, bạn nên " +"nâng cấp trình phục vụ lên một phiên bản được hỗ trợ." + +#: ../calendar/calendar.error.xml.h:62 +msgid "You have changed this appointment, but not yet saved them." +msgstr "Bạn đã sửa đổi cuộc hẹn này, nhưng chưa lưu lại." + +#: ../calendar/calendar.error.xml.h:63 ../calendar/calendar.error.xml.h:61 +msgid "You have made changes to this task, but not yet saved them." +msgstr "Bạn đã sửa đổi tác vụ này, nhưng chưa lưu lại." + +#: ../calendar/calendar.error.xml.h:64 ../calendar/calendar.error.xml.h:62 +msgid "Your calendars will not be available until Evolution is restarted." +msgstr "" +"Các lịch của bạn sẽ không sẵn sàng cho đến khi bạn khởi chạy lại Evolution." + +#: ../calendar/calendar.error.xml.h:65 ../calendar/calendar.error.xml.h:63 +msgid "Your tasks will not be available until Evolution is restarted." +msgstr "" +"Các tác vụ của bạn sẽ không sẵn sàng cho đến khi bạn khởi chạy lại Evolution." + +#: ../app/display.c:1149 +msgid "_Discard Changes" +msgstr "_Hủy thay đổi" + +#: ../calendar/calendar.error.xml.h:68 +msgid "_Save Changes" +msgstr "_Lưu thay đổi" + +# Variable: do not translate/ biến: đừng dịch +#: ../calendar/calendar.error.xml.h:70 ../calendar/calendar.error.xml.h:66 +msgid "{0}." +msgstr "{0}." + +#: ../smime/gui/component.c:48 ../modemlights/modemlights.glade.h:3 +#: ../lib/sunone-account.c:324 +msgid "Enter password" +msgstr "Hãy gõ mật khẩu" + +#: ../calendar/conduits/calendar/calendar-conduit.c:246 +msgid "Split Multi-Day Events:" +msgstr "Tách sự kiện nhiều ngày:" + +#: ../calendar/conduits/todo/todo-conduit.c:880 +msgid "Could not start evolution-data-server" +msgstr "Không thể khởi động evolution-data-server (máy phục vụ dữ liệu)." + +#: ../calendar/conduits/calendar/calendar-conduit.c:1477 +msgid "Could not read pilot's Calendar application block" +msgstr "Không thể đọc khối ứng dụng lịch của pilot." + +#: ../calendar/conduits/memo/memo-conduit.c:937 +#: ../calendar/conduits/memo/memo-conduit.c:940 +msgid "Could not read pilot's Memo application block" +msgstr "Không thể đọc khối ứng dụng Ghi nhớ của pilot." + +#: ../calendar/conduits/memo/memo-conduit.c:976 +#: ../calendar/conduits/memo/memo-conduit.c:979 +msgid "Could not write pilot's Memo application block" +msgstr "Không thể ghi khối ứng dụng Ghi nhớ của pilot." + +#: ../calendar/conduits/todo/todo-conduit.c:239 +#: ../calendar/conduits/todo/todo-conduit.c:234 +msgid "Default Priority:" +msgstr "Độ ưu tiên mặc định:" + +#: ../calendar/conduits/todo/todo-conduit.c:962 +msgid "Could not read pilot's ToDo application block" +msgstr "Không thể đọc khối ứng dụng ToDo (cần làm) của pilot." + +#: ../calendar/conduits/todo/todo-conduit.c:1151 +#: ../calendar/conduits/todo/todo-conduit.c:1154 +msgid "Could not write pilot's ToDo application block" +msgstr "Không thể ghi khối ứng dụng ToDo (cần làm) của pilot." + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:1 +#: ../plugins/itip-formatter/itip-formatter.c:1968 +msgid "Calendar and Tasks" +msgstr "Lịch và Tác vụ" + +#: ../calendar/gui/calendar-component.c:1307 +msgid "Calendars" +msgstr "Lịch" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:3 +msgid "Configure your timezone, Calendar and Task List here " +msgstr "Cấu hình múi giờ, Lịch và danh sách Tác vụ ở đây." + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:4 +msgid "Evolution Calendar and Tasks" +msgstr "Lịch và Tác vụ Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:5 +msgid "Evolution Calendar configuration control" +msgstr "Điều khiển cấu hình Lịch Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:6 +msgid "Evolution Calendar scheduling message viewer" +msgstr "Bộ xem thông báo lập lịch Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:7 +msgid "Evolution Calendar/Task editor" +msgstr "Bộ hiệu chỉnh Lịch/Tác vụ Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:8 +msgid "Evolution's Calendar component" +msgstr "Thành phần Lịch Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:9 +msgid "Evolution's Memos component" +msgstr "Thành phần Ghi nhớ của Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:10 +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:9 +msgid "Evolution's Tasks component" +msgstr "Thành phần Tác vụ Evolution" + +#: ../calendar/gui/GNOME_Evolution_Calendar.server.in.in.h:11 +msgid "Memo_s" +msgstr "Ghi _nhớ" + +#: ../calendar/gui/memos-component.c:998 ../calendar/gui/memos-control.c:340 +msgid "Memos" +msgstr "Ghi nhớ" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:12 +#: ../pan/prefs.c:1730 +msgid "Tasks" +msgstr "Tác vụ" + +#: ../src/GNOME_Evolution_BrainRead.server.in.in.h:8 +msgid "_Calendars" +msgstr "_Lịch" + +#: ../src/planner-task-view.c:264 +msgid "_Tasks" +msgstr "_Tác vụ" + +#: ../calendar/gui/alarm-notify/GNOME_Evolution_Calendar_AlarmNotify.server.in.in.h:1 +msgid "Evolution Calendar alarm notification service" +msgstr "Dịch vụ báo động Lịch Evolution" + +#: ../calendar/gui/alarm-notify/alarm-notify-dialog.c:248 +#: ../objects/chronogram/chronoline.c:164 +#: ../objects/chronogram/chronoref.c:146 +msgid "Start time" +msgstr "Thời điểm đầu" + +#: ../calendar/gui/alarm-notify/alarm-notify-dialog.c:356 +#: ../calendar/gui/alarm-notify/alarm-notify-dialog.c:347 +#, c-format +msgid "" +"%s\n" +"%s until %s" +msgstr "" +"%s\n" +"%s cho đến %s" + +#: ../calendar/gui/alarm-notify/alarm-notify.glade.h:1 +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:4 +#: ../applets/clock/clock.c:1116 +msgid "Appointments" +msgstr "Cuộc hẹn" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. Location +#: ../extensions/page-info/page-info.glade.h:19 ../glade/straw.glade.h:30 +#: ../storage/sunone-itip-view.c:727 +msgid "Location:" +msgstr "Địa điểm:" + +#: ../calendar/gui/alarm-notify/alarm-notify.glade.h:3 +msgid "Snooze _time:" +msgstr "Thời gian _ngủ :" + +#: ../calendar/gui/alarm-notify/alarm-notify.glade.h:5 +msgid "_Snooze" +msgstr "_Ngủ" + +#: ../calendar/gui/alarm-notify/alarm-notify.glade.h:7 +msgid "location of appointment" +msgstr "địa điểm cuộc hẹn" + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1127 +msgid "Calendars" +msgstr "Lịch" + +#: src/mainwin.cpp:1741 src/prefsdlg.cpp:1226 ../src/guikachu.glade.h:11 +#: ../src/preferences-win.cc:50 prefs_gui.c:334 po/silky.glade.h:143 +msgid "Preferences" +msgstr "Tùy thích" + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1206 +msgid "_Configure Alarms" +msgstr "_Cấu hình Báo động" + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1037 +msgid "No summary available." +msgstr "Không có tóm tắt" + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1048 +msgid "No description available." +msgstr "Không có mô tả." + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1056 +msgid "No location information available." +msgstr "Không có thông tin địa điểm." + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1414 +#, c-format +msgid "You have %d alarms" +msgstr "Bạn có %d bảo động" + +#: ../plug-ins/common/gtm.c:424 ../lib/message.c:80 ../lib/message.c:226 +#: ../src/mlview-validator-window.cc:443 ../widgets/gtk+.xml.in.h:215 +#: ../src/dialog-win-helpers.cc:378 app/gui-subs.c:589 +msgid "Warning" +msgstr "Cảnh báo" + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1590 +#: ../calendar/gui/alarm-notify/alarm-queue.c:1179 +msgid "" +"Evolution does not support calendar reminders with\n" +"email notifications yet, but this reminder was\n" +"configured to send an email. Evolution will display\n" +"a normal reminder dialog box instead." +msgstr "" +"Evolution chưa hỗ trợ bộ nhắc nhở lịch thông qua\n" +"thư điện tử, nhưng mà bộ nhắc nhở này đã được\n" +"cấu hình để gởi thư. Thay vào đó, Evolution\n" +"sẽ hiển thị một hộp thoại nhắc nhở thông thường." + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1616 +#: ../calendar/gui/alarm-notify/alarm-queue.c:1205 +#, c-format +msgid "" +"An Evolution Calendar reminder is about to trigger. This reminder is " +"configured to run the following program:\n" +"\n" +" %s\n" +"\n" +"Are you sure you want to run this program?" +msgstr "" +"Lịch Evolution sắp nhắc nhở bạn. Bộ nhắc nhở này được cấu hình để chạy những " +"chương trình sau:\n" +"\n" +" %s\n" +"\n" +"Bạn có chắc muốn chạy chương trình này không?" + +#: ../calendar/gui/alarm-notify/alarm-queue.c:1630 +#: ../calendar/gui/alarm-notify/alarm-queue.c:1219 +msgid "Do not ask me about this program again." +msgstr "Đừng hỏi tôi về chương trình này lần nữa." + +#: ../providers/evolution/gda-evolution-connection.c:100 +msgid "Could not initialize Bonobo" +msgstr "Không thể khởi động Bonobo" + +#: ../calendar/gui/alarm-notify/notify-main.c:153 +#: ../calendar/gui/alarm-notify/notify-main.c:150 +msgid "Could not create the alarm notify service factory" +msgstr "Không thể tạo bộ tạo dịch vụ báo động" + +#: ../calendar/gui/alarm-notify/util.c:41 +msgid "invalid time" +msgstr "thời gian không hợp lệ" + +#. Can't be zero +#: ../calendar/gui/alarm-notify/util.c:58 ../calendar/gui/misc.c:105 +#, c-format +msgid "(%d seconds)" +msgstr "(%d giây)" + +#: ../calendar/gui/alarm-notify/util.c:64 ../calendar/gui/misc.c:111 +#, c-format +msgid "(%d %s %d %s)" +msgstr "(%d %s %d %s)" + +#: ../app/display/gimpdisplayshell-close.c:279 ../bin/ical-dump.c:81 +msgid "second" +msgstr "giây" + +#: ../src/smart-playlist-dialog.c:169 ../gncal/gnomecal-prefs.c:1444 +#: ../gncal/gnomecal-prefs.c:1467 +msgid "seconds" +msgstr "giây" + +#: ../calendar/gui/alarm-notify/util.c:66 ../calendar/gui/misc.c:113 +#, c-format +msgid "(%d %s)" +msgstr "(%d %s)" + +#: ../calendar/gui/alarm-notify/util.c:77 ../calendar/gui/misc.c:124 +#, c-format +msgid " %u second" +msgstr " %u giây" + +#: ../calendar/gui/alarm-notify/util.c:77 ../calendar/gui/misc.c:124 +#, c-format +msgid " %u seconds" +msgstr " %u giây" + +#: ../calendar/gui/alarm-notify/util.c:79 ../calendar/gui/misc.c:126 +#, c-format +msgid " %u minute" +msgstr " %u phút" + +#: ../calendar/gui/alarm-notify/util.c:79 ../calendar/gui/misc.c:126 +#, c-format +msgid " %u minutes" +msgstr " %u phút" + +#: ../calendar/gui/alarm-notify/util.c:81 ../calendar/gui/misc.c:128 +#, c-format +msgid "%u hour" +msgstr "%u giờ" + +#: ../calendar/gui/alarm-notify/util.c:81 ../calendar/gui/misc.c:128 +#, c-format +msgid "%u hours" +msgstr "%u giờ" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:1 +msgid "Alarm programs" +msgstr "Chương trình báo động" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:2 +msgid "Ask for confirmation when deleting items" +msgstr "Hỏi xác thực khi xoá bỏ mục" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:3 +msgid "Background color of tasks that are due today, in \"#rrggbb\" format." +msgstr "Màu nền của mọi tác vụ hết hạn hôm nay, có dạng « #rrggbb »." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:4 +msgid "Background color of tasks that are overdue, in \"#rrggbb\" format." +msgstr "Màu nền của mọi tác vụ quá hạn, có dạng « #rrggbb »." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:5 +msgid "Calendars to run alarms for" +msgstr "Lịch cần chạy báo động" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:6 +msgid "" +"Color to draw the Marcus Bains Line in the Time bar (empty for default)." +msgstr "" +"Màu cần vẽ Dòng Marcus Bains trong thanh Thời gian (bỏ rỗng để chọn mặc định)" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:7 +msgid "Color to draw the Marcus Bains line in the Day View." +msgstr "Màu cần vẽ Dòng Marcus Bains trong khung xem Ngày" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:8 +msgid "Compress weekends in month view" +msgstr "Nén các ngày cuối tuần trong khung xem tháng" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:9 +msgid "Confirm expunge" +msgstr "Xác nhận khi xoá hẳn" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:10 +msgid "Days on which the start and end of work hours should be indicated." +msgstr "Ngày cần ngụ ý giờ bắt đầu và kết thúc đều làm việc." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:11 +msgid "Default appointment reminder" +msgstr "Bộ nhắc nhở cuộc hẹn mặc định" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:12 +msgid "Default reminder units" +msgstr "Đơn vị nhắc nhở mặc định" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:13 +msgid "Default reminder value" +msgstr "Giá trị nhắc nhở mặc định" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:14 +msgid "Free/busy server urls" +msgstr "Địa chỉ Mạng của máy phục vụ Rảnh/Bận" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:15 +msgid "Free/busy template url" +msgstr "Địa chỉ Mạng mẫu Rảnh/Bận" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:16 +msgid "Hide completed tasks" +msgstr "Ẩn mọi tác vụ hoàn tất" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:17 +msgid "Hide task units" +msgstr "Ẩn đơn vị tác vụ" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:18 +msgid "Hide task value" +msgstr "Ẩn giá trị tác vụ" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:19 +msgid "Horizontal pane position" +msgstr "Ví trị ô cửa sổ ngang" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:20 +msgid "Hour the workday ends on, in twenty four hour format, 0 to 23." +msgstr "Giờ kết thúc ngày làm việc, có dạng 24 giờ (0-23)." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:21 +msgid "Hour the workday starts on, in twenty four hour format, 0 to 23." +msgstr "Giờ bắt đầu ngày làm việc, có dạng 24 giờ (0-23)." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:22 +msgid "Intervals shown in Day and Work Week views, in minutes." +msgstr "" +"Hộp thời gian được hiển thị trong khung xem Ngày/Tuần làm việc, theo phút" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:23 +msgid "Last alarm time" +msgstr "Giờ báo động cuối cùng" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:24 +msgid "List of server urls for free/busy publishing." +msgstr "Danh sách các địa chỉ Mạng máy phục vụ cho xuất thông tin Rảnh/Bận" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:25 +msgid "Marcus Bains Line" +msgstr "Dòng Marcus Bains" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:26 +msgid "Marcus Bains Line Color - Day View" +msgstr "Màu Dòng Marcus Bains — Khung xem ngày" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:27 +msgid "Marcus Bains Line Color - Time bar" +msgstr "Màu Dòng Marcus Bains — Thanh thời gian" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:28 +msgid "Minute the workday ends on, 0 to 59." +msgstr "Phút kết thúc ngày làm việc, 0-59." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:29 +msgid "Minute the workday starts on, 0 to 59." +msgstr "Phút bắt đầu ngày làm việc, 0-59." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:30 +msgid "Month view horizontal pane position" +msgstr "Vị trí của ô cửa sổ ngang trong khung xem tháng" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:31 +msgid "Month view vertical pane position" +msgstr "Vị trí của ô cửa sổ dọc trong khung xem tháng" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:32 +msgid "Number of units for determining for a default reminder." +msgstr "Tổng số đơn vị để quyết định lúc nào nhắc nhở mặc định." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:33 +msgid "Number of units for determining when to hide tasks." +msgstr "Tổng số đơn vị để quyết định lúc nào nên ẩn cộng việc." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:34 +msgid "Overdue tasks color" +msgstr "Màu của tác vụ quá hạn" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:35 +msgid "" +"Position of the horizontal pane, between the date navigator calendar and the " +"task list when not in the month view, in pixels." +msgstr "" +"Ví trị của ô cửa sổ ngang, giữa lịch duyệt ngày và danh sách tác vụ khi " +"không phải trong khung xem tháng, theo điểm ảnh." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:36 +msgid "" +"Position of the horizontal pane, between the view and the date navigator " +"calendar and task list in the month view, in pixels." +msgstr "" +"Ví trị của ô cửa sổ ngang, giữa khung xem và lịch duyệt ngày và danh sách " +"tác vụ khi trong khung xem tháng, theo điểm ảnh." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:37 +msgid "" +"Position of the vertical pane, between the task list and the task preview " +"pane, in pixels." +msgstr "" +"Vị trí của ô cửa sổ dọc, giữa danh sách tác vụ và khung xem cộng việc, theo " +"điểm ảnh." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:38 +msgid "" +"Position of the vertical pane, between the view and the date navigator " +"calendar and task list in the month view, in pixels." +msgstr "" +"Ví trị của ô cửa sổ dọc, giữa khung xem và lịch duyệt ngày và danh sách công " +"việc khi trong khung xem tháng, theo điểm ảnh." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:39 +msgid "" +"Position of the vertical pane, between the view and the date navigator " +"calendar and task list when not in the month view, in pixels." +msgstr "" +"Ví trị của ô cửa sổ ngang, giữa lịch duyệt ngày và danh sách tác vụ khi " +"không phải trong khung xem tháng, theo điểm ảnh." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:40 +msgid "Programs that are allowed to be run by alarms." +msgstr "Chương trình có chạy được với bảo động" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:41 +msgid "Show RSVP field in the event/task/meeting editor" +msgstr "Hiện trường RSVP trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:42 +msgid "Show Role field in the event/task/meeting editor" +msgstr "Hiện trường Vai trò trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:43 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:41 +msgid "Show appointment end times in week and month views" +msgstr "Hiện thời điểm kết thúc cuộc hẹn trong khung xem tuần và tháng đều" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:44 +msgid "Show categories field in the event/meeting/task editor" +msgstr "Hiện trường Hạng trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:45 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:42 +msgid "Show display alarms in notification tray" +msgstr "Hiển thị báo động trong khay thông báo" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:46 +msgid "Show status field in the event/task/meeting editor" +msgstr "Hiện trường Trạng thái trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:47 +#: ../mail/evolution-mail.schemas.in.in.h:73 +msgid "Show the \"Preview\" pane" +msgstr "Hiện ô « Xem thử »" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:48 +#: ../mail/evolution-mail.schemas.in.in.h:74 +msgid "Show the \"Preview\" pane." +msgstr "Hiện ô « Xem thử »." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:49 +msgid "Show timezone field in the event/meeting editor" +msgstr "Hiện trường Múi giờ trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:50 +msgid "Show type field in the event/task/meeting editor" +msgstr "Hiện trường Kiểu trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:51 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:43 +msgid "Show week numbers in date navigator" +msgstr "Hiện số thứ tự tuần trong bộ duyệt ngày" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:52 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:44 +msgid "Tasks due today color" +msgstr "Màu của tác vụ hết hạn vào hôm nay" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:53 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:45 +msgid "Tasks vertical pane position" +msgstr "Ví trị ô cửa sổ dọc tác vụ" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:54 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:46 +msgid "" +"The default timezone to use for dates and times in the calendar, as an " +"untranslated Olsen timezone database location like \"America/New York\"." +msgstr "" +"Múi giờ mặc định cần dùng cho ngày và giờ trong lịch, là ví trị cơ sở dữ " +"liệu kiểu Olsen chưa dịch như « Asia/Hanoi » (Châu Á/Hà nội)." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:56 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:48 +#, no-c-format +msgid "" +"The url template to use as a free/busy data fallback, %u is replaced by the " +"user part of the mail address and %d is replaced by the domain." +msgstr "" +"Mẫu địa chỉ Mạng cần dùng là dữ liệu Rảnh/Bận phục hồi: « %u » được thay thế " +"bằng phần người dùng của địa chỉ thư, và « %d » được thay thế bằng miền của " +"địa chỉ đó." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:57 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:49 +msgid "Time divisions" +msgstr "Chia thời gian" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:58 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:50 +msgid "Time the last alarm ran, in time_t." +msgstr "Giờ đã bảo động cuối cùng, theo time_t" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:59 +#: timeanddate.c:590 timeanddate.c:599 ../Sensors/Clock/__init__.py:106 +#: src/settings.c:1270 +msgid "Timezone" +msgstr "Múi giờ" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:60 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:52 +msgid "Twenty four hour time format" +msgstr "Định dạng thời gian 24 giờ" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:61 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:53 +msgid "Units for a default reminder, \"minutes\", \"hours\" or \"days\"." +msgstr "Đơn vị cho bộ nhắc nhở mặc định: « phút », « giờ » hay « ngày »." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:62 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:54 +msgid "" +"Units for determining when to hide tasks, \"minutes\", \"hours\" or \"days\"." +msgstr "" +"Đơn vị để quyết định lúc nào ẩn tác vụ : « phút », « giờ » hay « ngày »." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:64 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:56 +msgid "Week start" +msgstr "Tuần bắt đầu" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:65 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:57 +msgid "Weekday the week starts on, from Sunday (0) to Saturday (6)." +msgstr "Hôm bắt đầu tuần, từ Chủ Nhật (0) đến Thứ Bảy (6)." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:66 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:58 +msgid "Whether or not to use the notification tray for display alarms." +msgstr "Có nên dùng khay thông báo để hiển thị báo động hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:67 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:59 +msgid "Whether to ask for confirmation when deleting an appointment or task." +msgstr "Có nên hỏi xác nhận khi xoá bỏ cuộc hẹn hay tác vụ hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:68 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:60 +msgid "Whether to ask for confirmation when expunging appointments and tasks." +msgstr "Có nên hỏi xác nhận khi xoá hắn cuộc hẹn và tác vụ hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:69 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:61 +msgid "" +"Whether to compress weekends in the month view, which puts Saturday and " +"Sunday in the space of one weekday." +msgstr "" +"Có nên nén những ngày cuối tuần trong khung xem tháng, mà hiển thị hai ngày " +"Thứ Bảy và Chủ Nhật đều là cùng cách của một ngày tuần." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:70 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:62 +msgid "Whether to display the end time of events in the week and month views." +msgstr "" +"Có nên hiển thị thời điểm kết thúc sự kiện trong khung xem tuần và tháng đều " +"hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:71 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:63 +msgid "" +"Whether to draw the Marcus Bains Line (line at current time) in the calendar." +msgstr "" +"Có nên vẽ Dòng Marcus Bains (dòng tại giờ hiện có) trong lịch hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:72 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:64 +msgid "Whether to hide completed tasks in the tasks view." +msgstr "Có nên ẩn mọi tác vụ đã hoàn tất trong khung xem tác vụ hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:73 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:65 +msgid "Whether to set a default reminder for appointments." +msgstr "Có nên lập bộ nhắc nhở mặc định cho mọi cuộc hẹn hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:74 +msgid "Whether to show RSVP field in the event/task/meeting editor" +msgstr "" +"Có nên hiển thị trường RSVP trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện hay " +"không" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:75 +msgid "Whether to show categories field in the event/meeting editor" +msgstr "" +"Có nên hiển thị trường loại trong bộ hiệu chỉnh cuộc họp/sự kiện hay không" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:76 +msgid "Whether to show role field in the event/task/meeting editor" +msgstr "" +"Có nên hiển thị trường Vai trò trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện " +"hay không" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:77 +msgid "Whether to show status field in the event/task/meeting editor" +msgstr "" +"Có nên hiển thị trường trạng thái trong bộ hiệu chỉnh cuộc họp/tác vụ/dữ " +"kiện hay không" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:78 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:66 +msgid "" +"Whether to show times in twenty four hour format instead of using am/pm." +msgstr "" +"Có nên hiển thị giờ dạng 24-giờ thay vào dùng am/pm (buổi sáng/buổi chiều-" +"tối) hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:79 +msgid "Whether to show timezone field in the event/meeting editor" +msgstr "" +"Có nên hiển thị trường múi giờ trong bộ hiệu chỉnh cuộc họp/sự kiện hay không" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:80 +msgid "Whether to show type field in the event/task/meeting editor" +msgstr "" +"Có nên hiển thị trường kiểu trong bộ hiệu chỉnh cuộc họp/tác vụ/sự kiện hay " +"không" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:81 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:67 +msgid "Whether to show week numbers in the date navigator." +msgstr "Có nên hiển thị số thứ tự tuần trong bộ duyệt ngày hay không." + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:82 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:68 +msgid "Work days" +msgstr "Ngày làm việc" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:83 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:69 +msgid "Workday end hour" +msgstr "Giờ kết thúc ngày làm việc" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:84 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:70 +msgid "Workday end minute" +msgstr "Phút kết thúc ngày làm việc" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:85 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:71 +msgid "Workday start hour" +msgstr "Giờ bắt đầu ngày làm việc" + +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:86 +#: ../calendar/gui/apps_evolution_calendar.schemas.in.in.h:72 +msgid "Workday start minute" +msgstr "Phút bắt đầu ngày làm việc" + +#: ../calendar/gui/cal-search-bar.c:48 ../calendar/gui/cal-search-bar.c:49 +msgid "Summary contains" +msgstr "Tóm tắt chứa" + +#: ../calendar/gui/cal-search-bar.c:49 ../calendar/gui/cal-search-bar.c:50 +msgid "Description contains" +msgstr "Mô tả chứa" + +#: ../calendar/gui/cal-search-bar.c:51 +msgid "Comment contains" +msgstr "Ghi chú chứa" + +#: ../calendar/gui/cal-search-bar.c:52 +msgid "Location contains" +msgstr "Địa điểm chứa" + +#: ../calendar/gui/cal-search-bar.c:350 ../camel/camel-vee-store.c:100 +#: ../camel/camel-vee-store.c:343 +msgid "Unmatched" +msgstr "Không khớp" + +#: ../libgnomeui/gnome-dateedit.c:775 ../glade/gbwidgets/gbcalendar.c:266 +#: ../widgets/gtk+.xml.in.h:28 ../src/orca/rolenames.py:168 +msgid "Calendar" +msgstr "Lịch" + +#: ../calendar/gui/calendar-commands.c:348 +#: ../calendar/gui/calendar-commands.c:354 +msgid "" +"This operation will permanently erase all events older than the selected " +"amount of time. If you continue, you will not be able to recover these " +"events." +msgstr "" +"Thao tác này sẽ xoá bỏ hoàn toàn mọi sự kiện trước khoảng thời gian được " +"chọn. Nếu bạn tiếp tục thì sẽ không thể phục hồi những tác vụ này." + +#: ../calendar/gui/calendar-commands.c:354 +#: ../calendar/gui/calendar-commands.c:360 +msgid "Purge events older than" +msgstr "Tẩy mọi sự kiện trước" + +#: ../src/smart-playlist-dialog.c:162 +msgid "days" +msgstr "ngày" + +#: ../calendar/gui/migration.c:582 +msgid "On The Web" +msgstr "Trên Mạng" + +#: ../calendar/gui/calendar-component.c:251 ../calendar/gui/migration.c:396 +#: ../calendar/gui/calendar-component.c:252 ../calendar/gui/migration.c:391 +msgid "Birthdays & Anniversaries" +msgstr "Sinh nhật và Kỷ niệm" + +#: ../calendar/gui/calendar-component.c:258 ../Sensors/Weather/__init__.py:129 +msgid "Weather" +msgstr "Thời tiết" + +#: ../calendar/gui/calendar-component.c:534 +msgid "_New Calendar" +msgstr "Lịch _mới" + +#: ../calendar/gui/calendar-component.c:838 +#: ../calendar/gui/calendar-component.c:858 +msgid "Failed upgrading calendars." +msgstr "Không cập nhật lịch được." + +#: ../calendar/gui/calendar-component.c:1137 +#: ../calendar/gui/calendar-component.c:1154 +#, c-format +msgid "Unable to open the calendar '%s' for creating events and meetings" +msgstr "Không thể mở lịch « %s » để tạo sự kiện và cuộc họp." + +#: ../calendar/gui/calendar-component.c:1150 +#: ../calendar/gui/calendar-component.c:1170 +msgid "There is no calendar available for creating events and meetings" +msgstr "Không có lịch nào sẵn sàng để tạo sự kiện và cuộc họp." + +#: ../calendar/gui/calendar-component.c:1264 +#: ../calendar/gui/calendar-component.c:1282 +msgid "Calendar Source Selector" +msgstr "Bộ chọn nguồn lịch" + +#: ../calendar/gui/calendar-component.c:1455 main.c:261 +#: ../calendar/gui/calendar-component.c:1473 +msgid "New appointment" +msgstr "Cuộc hẹn mới" + +#: ../calendar/gui/calendar-component.c:1456 +#: ../calendar/gui/calendar-component.c:1474 +msgid "_Appointment" +msgstr "_Cuộc hẹn" + +#: ../calendar/gui/calendar-component.c:1457 +#: ../calendar/gui/calendar-component.c:1475 +#: ../gncal/gnomecal-main-window.c:537 ../gncal/gnomecal-main-window.c:571 +msgid "Create a new appointment" +msgstr "Tạo cuộc hẹn mới" + +#: ../calendar/gui/calendar-component.c:1463 +#: ../calendar/gui/calendar-component.c:1481 +msgid "New meeting" +msgstr "Cuộc họp mới" + +#: ../calendar/gui/calendar-component.c:1464 +#: ../calendar/gui/calendar-component.c:1482 +msgid "M_eeting" +msgstr "_Cuộc họp" + +#: ../calendar/gui/calendar-component.c:1465 +#: ../calendar/gui/calendar-component.c:1483 +msgid "Create a new meeting request" +msgstr "Tạo yêu cầu cuộc họp mới" + +#: ../calendar/gui/calendar-component.c:1471 +#: ../calendar/gui/calendar-component.c:1489 +msgid "New all day appointment" +msgstr "Cuộc hẹn nguyên ngày mới" + +#: ../calendar/gui/calendar-component.c:1472 +#: ../calendar/gui/calendar-component.c:1490 +msgid "All Day A_ppointment" +msgstr "Cuộc hẹn _nguyên ngày" + +#: ../calendar/gui/calendar-component.c:1473 +#: ../calendar/gui/calendar-component.c:1491 +msgid "Create a new all-day appointment" +msgstr "Tạo cuộc hẹn nguyên ngày mới" + +#: ../calendar/gui/calendar-component.c:1479 +#: ../calendar/gui/calendar-component.c:1497 +msgid "New calendar" +msgstr "Lịch mới" + +#: ../calendar/gui/calendar-component.c:1498 +msgid "Cale_ndar" +msgstr "_Lịch" + +#: ../gncal/gnomecal-main-window.c:522 +msgid "Create a new calendar" +msgstr "Tạo lịch mới" + +#: ../calendar/gui/calendar-view-factory.c:109 main.c:284 +#: ../gncal/calendar-widget.c:377 +msgid "Day View" +msgstr "Xem ngày" + +#: ../calendar/gui/calendar-view-factory.c:112 +msgid "Work Week View" +msgstr "Xem tuần làm việc" + +#: ../calendar/gui/calendar-view-factory.c:115 main.c:292 +#: ../gncal/calendar-widget.c:390 +msgid "Week View" +msgstr "Xem tuần" + +#: ../calendar/gui/calendar-view-factory.c:118 main.c:300 +#: ../gncal/calendar-widget.c:404 +msgid "Month View" +msgstr "Xem tháng" + +#: ../calendar/gui/comp-editor-factory.c:420 +msgid "Error while opening the calendar" +msgstr "Gặp lỗi khi mở lịch" + +#: ../calendar/gui/comp-editor-factory.c:426 +msgid "Method not supported when opening the calendar" +msgstr "Không hỗ trợ phương thức đó khi mở lịch này." + +#: ../calendar/gui/comp-editor-factory.c:432 +msgid "Permission denied to open the calendar" +msgstr "Không đủ quyền truy cập để mở lịch" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:1 +msgid "Alarm" +msgstr "Báo động" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:2 ../eog.glade.h:7 +#: ../gnome-screenshot/gnome-panel-screenshot.glade.h:2 +msgid "Options" +msgstr "Tùy chọn" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:3 +msgid "Repeat" +msgstr "Lặp lại" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:4 +msgid "Add Alarm" +msgstr "Thêm Báo động" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:5 +msgid "Custom _message" +msgstr "Thông điệp tự _chọn" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:6 +msgid "Custom alarm sound" +msgstr "Âm thanh báo động tự chọn" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:7 +msgid "Mes_sage:" +msgstr "_Thông điệp:" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:8 +#: ../calendar/gui/e-alarm-list.c:444 +msgid "Play a sound" +msgstr "Phát âm thanh" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:9 +#: ../calendar/gui/e-alarm-list.c:448 +msgid "Pop up an alert" +msgstr "Bật lên báo động" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:10 +#: ../calendar/gui/e-alarm-list.c:456 +msgid "Run a program" +msgstr "Chạy chương trình" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:11 +msgid "Send To:" +msgstr "Gởi đến:" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:12 +#: ../calendar/gui/e-alarm-list.c:452 +msgid "Send an email" +msgstr "Gởi thư" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:13 +msgid "_Arguments:" +msgstr "_Đối số :" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:14 src/interface.c:240 +msgid "_Program:" +msgstr "_Chương trình:" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:15 +msgid "_Repeat the alarm" +msgstr "_Lặp lại báo động" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:16 +msgid "_Sound:" +msgstr "_Âm thanh:" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:17 +#: ../plug-ins/winsnap/winsnap.c:911 ../ui/prefs.glade.h:39 +msgid "after" +msgstr "sau" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:18 +msgid "before" +msgstr "trước (khi)" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:19 +#: ../calendar/gui/dialogs/recurrence-page.glade.h:7 +#: ../data/SoftwareProperties.glade.h:9 +msgid "day(s)" +msgstr "ngày" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:21 +msgid "end of appointment" +msgstr "kết thúc cuộc hẹn" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:22 +msgid "extra times every" +msgstr "lần thêm nữa mỗi" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:23 +msgid "hour(s)" +msgstr "giờ" + +#: ../src/smart-playlist-dialog.c:184 +msgid "hours" +msgstr "giờ" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:25 +msgid "minute(s)" +msgstr "phút" + +#: ../calendar/gui/dialogs/alarm-dialog.glade.h:27 +msgid "start of appointment" +msgstr "bắt đầu cuộc hẹn" + +#: ../calendar/gui/dialogs/alarm-list-dialog.c:200 +#: ../calendar/gui/dialogs/alarm-list-dialog.c:199 +msgid "Action/Trigger" +msgstr "Hành động/Gây ra" + +#: ../calendar/gui/dialogs/alarm-list-dialog.glade.h:2 +#: ../calendar/gui/dialogs/event-page.glade.h:4 +#: ../ui/evolution-event-editor.xml.h:1 ../gncal/gnomecal-prefs.c:1861 +#: prefs_gui.c:376 +msgid "Alarms" +msgstr "Báo động" + +#: ../composer/e-msg-composer-select-file.c:116 +msgid "Suggest automatic display of attachment" +msgstr "Đề nghị tự động hiển thị đính kèm" + +#: ../calendar/gui/dialogs/cal-attachment-select-file.c:190 +#: ../composer/e-msg-composer-select-file.c:238 +msgid "Attach file(s)" +msgstr "Đính kèm tập tin" + +# #: ../widgets/misc/e-attachment.glade.h:2 +msgid "Attachment Properties" +msgstr "Thuộc tính đính kèm" + +#: ../plug-ins/gimpressionist/presets.c:646 ../app/sheets_dialog.c:599 +#: ../app/sheets_dialog.c:692 ../app/sheets_dialog.c:600 +#: ../app/sheets_dialog.c:693 ../glade/glade_atk.c:640 ../src/gtkfunc.c:432 +msgid "Description:" +msgstr "Mô tả:" + +#: ../extensions/page-info/page-info.glade.h:20 src/gtkam-info.c:474 +msgid "MIME type:" +msgstr "Kiểu MIME:" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:1 +msgid "" +"60 minutes\n" +"30 minutes\n" +"15 minutes\n" +"10 minutes\n" +"05 minutes" +msgstr "" +"60 phút\n" +"30 phút\n" +"15 phút\n" +"10 phút\n" +"05 phút" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:6 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:10 +msgid "Publishing" +msgstr "Xuất" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:8 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:15 +#, no-c-format +msgid "" +"%u and %d will be replaced by user and domain from the email address." +msgstr "" +"« %u » và « %d » sẽ được thay thế bằng người dùng và miền riêng từng từ " +"địa chỉ thư." + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:9 +#: ../mail/mail-config.glade.h:9 +msgid "Alerts" +msgstr "Báo động" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:10 +msgid "Default Free/Busy Server" +msgstr "Máy phục vụ Rảnh/Bận mặc định" + +#: ../extensions/actions/action-properties.glade.h:2 +msgid "General" +msgstr "Chung" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:12 +msgid "Task List" +msgstr "Danh sách Tác vụ" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:13 +msgid "Time" +msgstr "Giờ" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:14 +msgid "Work Week" +msgstr "Tuần làm việc" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:15 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:19 +msgid "Day _ends:" +msgstr "Ngày _kết thúc:" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:22 +msgid "E_nable" +msgstr "_Bật" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:23 +msgid "Free/Busy" +msgstr "Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:733 ../calendar.inc.php:12 +#: datebook_gui.c:1554 +msgid "Friday" +msgstr "Thứ Sáu" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:21 +msgid "" +"Minutes\n" +"Hours\n" +"Days" +msgstr "" +"Phút\n" +"Giờ\n" +"Ngày" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:24 +#: ../calendar/gui/dialogs/recurrence-page.c:1046 +#: ../calendar/gui/e-itip-control.c:729 ../calendar.inc.php:10 +#: datebook_gui.c:1550 prefs.c:414 +msgid "Monday" +msgstr "Thứ Hai" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:25 +msgid "" +"Monday\n" +"Tuesday\n" +"Wednesday\n" +"Thursday\n" +"Friday\n" +"Saturday\n" +"Sunday" +msgstr "" +"Thứ Hai\n" +"Thứ Ba\n" +"Thứ Tư\n" +"Thứ Năm\n" +"Thứ Sáu\n" +"Thứ Bảy\n" +"Chủ Nhật" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:32 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:29 +msgid "Publishing Table" +msgstr "Bảng xuất" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:33 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:30 +msgid "S_un" +msgstr "_CN" + +#: ../calendar/gui/e-itip-control.c:734 ../calendar.inc.php:13 +#: datebook_gui.c:1555 +msgid "Saturday" +msgstr "Thứ Bảy" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:35 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:32 +msgid "Sh_ow a reminder" +msgstr "_Hiện bộ nhắc nhở" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:36 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:33 +msgid "Show week _numbers in date navigator" +msgstr "Hiện _số thứ tự tuần trong bộ duyệt ngày" + +#: ../calendar/gui/e-itip-control.c:728 ../calendar.inc.php:10 +#: datebook_gui.c:1549 prefs.c:413 +msgid "Sunday" +msgstr "Chủ Nhật" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:38 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:35 +msgid "T_asks due today:" +msgstr "_Tác vụ hết hạn vào hôm nay:" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:39 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:36 +msgid "T_hu" +msgstr "_Năm" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:40 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:37 +msgid "Template:" +msgstr "Mẫu :" + +#: ../calendar/gui/e-itip-control.c:732 ../calendar.inc.php:12 +#: datebook_gui.c:1553 +msgid "Thursday" +msgstr "Thứ Năm" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:39 +msgid "Time _zone:" +msgstr "Múi _giờ :" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:43 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:40 +msgid "Time format:" +msgstr "Dạng thức giờ :" + +#: ../calendar/gui/e-itip-control.c:730 ../calendar.inc.php:11 +#: datebook_gui.c:1551 +msgid "Tuesday" +msgstr "Thứ Ba" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:45 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:42 +msgid "W_eek starts:" +msgstr "Tuần _bắt đầu :" + +#: ../calendar/gui/e-itip-control.c:731 ../calendar.inc.php:11 +#: datebook_gui.c:1552 +msgid "Wednesday" +msgstr "Thứ Tư" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:47 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:44 +msgid "Work days:" +msgstr "Ngày làm việc:" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:48 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:45 +msgid "_12 hour (AM/PM)" +msgstr "_12 giờ (AM/PM: sáng/chiều)" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:49 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:46 +msgid "_24 hour" +msgstr "_24 giờ" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:50 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:47 +msgid "_Add URL" +msgstr "_Thêm địa chỉ Mạng" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:51 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:48 +msgid "_Ask for confirmation when deleting items" +msgstr "_Hỏi xác thực khi xoá bỏ mục" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:52 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:49 +msgid "_Compress weekends in month view" +msgstr "_Nén các ngày cuối tuần trong khung xem tháng" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:53 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:50 +msgid "_Day begins:" +msgstr "_Ngày bắt đầu :" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:55 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:52 +msgid "_Fri" +msgstr "_Sáu" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:56 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:53 +msgid "_Hide completed tasks after" +msgstr "Ẩ_n tác vụ hoàn thành sau" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:57 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:54 +msgid "_Mon" +msgstr "T_2" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:58 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:55 +msgid "_Overdue tasks:" +msgstr "Tác vụ _quá hạn:" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:59 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:56 +msgid "_Sat" +msgstr "T_7" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:60 +msgid "_Show appointment end times in week and month view" +msgstr "_Hiện thời điểm kết thúc cuộc hẹn trong khung xem tuần và tháng đều" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:61 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:58 +msgid "_Time divisions:" +msgstr "_Chia thời gian:" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:62 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:59 +msgid "_Tue" +msgstr "_Ba" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:63 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:60 +msgid "_Wed" +msgstr "_Tư" + +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:64 +#: ../calendar/gui/dialogs/cal-prefs-dialog.glade.h:61 +msgid "before every appointment" +msgstr "trước khi mỗi cuộc hẹn" + +#: ../calendar/gui/dialogs/calendar-setup.c:287 +msgid "Copy calendar contents locally for offline operation" +msgstr "Sao chép nội dung lịch về máy để phục vụ các thao tác ngoại tuyến." + +#: ../calendar/gui/dialogs/calendar-setup.c:290 +msgid "Copy task list contents locally for offline operation" +msgstr "" +"Sao chép nội dung danh sách tác vụ về máy để phục vụ các thao tác ngoại tuyến" + +#: ../calendar/gui/dialogs/calendar-setup.c:292 +msgid "Copy memo list contents locally for offline operation" +msgstr "" +"Sao chép nội dung danh sách ghi nhớ về máy để phục vụ các thao tác ngoại " +"tuyến" + +#: src/ui.glade.h:18 +msgid "C_olor:" +msgstr "_Màu :" + +#: ../calendar/gui/dialogs/calendar-setup.c:380 +msgid "Tasks List" +msgstr "Danh sách tác vụ" + +#: ../calendar/gui/dialogs/calendar-setup.c:391 +msgid "Memos List" +msgstr "Danh sách Ghi nhớ" + +#: ../calendar/gui/dialogs/calendar-setup.c:460 +#: ../calendar/gui/dialogs/calendar-setup.c:449 +msgid "Calendar Properties" +msgstr "Thuộc tính lịch" + +#: ../data/glade/calendar-dialog.glade.h:23 +msgid "New Calendar" +msgstr "Lịch mới" + +#: ../calendar/gui/dialogs/calendar-setup.c:528 +#: ../calendar/gui/dialogs/calendar-setup.c:517 +msgid "Task List Properties" +msgstr "Thuộc tính danh sách tác vụ" + +#: ../calendar/gui/dialogs/calendar-setup.c:594 +#: ../calendar/gui/memos-component.c:448 +msgid "New Memo List" +msgstr "Danh sách ghi nhớ mới" + +#: ../calendar/gui/dialogs/calendar-setup.glade.h:2 +msgid "Add Calendar" +msgstr "Thêm Lịch" + +#: ../calendar/gui/dialogs/calendar-setup.glade.h:3 +msgid "Add Task List" +msgstr "Thêm Danh sách Tác vụ" + +#: ../ui/prefs.glade.h:32 +msgid "Pick a color" +msgstr "Chọn màu" + +#: ../calendar/gui/dialogs/calendar-setup.glade.h:6 +msgid "_Add Calendar" +msgstr "_Thêm Lịch" + +#: ../calendar/gui/dialogs/calendar-setup.glade.h:7 +msgid "_Add Task List" +msgstr "Th_êm Danh sách Tác vụ" + +#: ../plugins/calendar-weather/calendar-weather.c:548 +msgid "_Refresh:" +msgstr "_Làm tươi:" + +#: ../plugins/calendar-http/calendar-http.c:120 ../src/gtkfunc.c:611 +msgid "_URL:" +msgstr "Địa chỉ _Mạng:" + +#: ../src/planner-format.c:379 ../src/smart-playlist-dialog.c:163 +msgid "weeks" +msgstr "tuần" + +#: ../calendar/gui/dialogs/changed-comp.c:60 +msgid "This event has been deleted." +msgstr "Sự kiện này bị xoá bỏ." + +#: ../calendar/gui/dialogs/changed-comp.c:64 +msgid "This task has been deleted." +msgstr "Tác vụ này bị xoá bỏ." + +#: ../calendar/gui/dialogs/changed-comp.c:68 +msgid "This journal entry has been deleted." +msgstr "Mục nhật ký này bị xoá bỏ." + +#: ../calendar/gui/dialogs/changed-comp.c:77 +#, c-format +msgid "%s You have made changes. Forget those changes and close the editor?" +msgstr "" +"%s Bạn đã tạo ra một vài thay đổi. Bỏ qua những thay đổi này và đóng bộ " +"biên soạn không?" + +#: ../calendar/gui/dialogs/changed-comp.c:79 +#, c-format +msgid "%s You have made no changes, close the editor?" +msgstr "%s Bạn chưa thay đổi gì, đóng bộ biên soạn?" + +#: ../calendar/gui/dialogs/changed-comp.c:84 +msgid "This event has been changed." +msgstr "Sự kiện này đã được thay đổi" + +#: ../calendar/gui/dialogs/changed-comp.c:88 +msgid "This task has been changed." +msgstr "Tác vụ này đã được thay đổi" + +#: ../calendar/gui/dialogs/changed-comp.c:92 +msgid "This journal entry has been changed." +msgstr "Mục nhật ký này đã được thay đổi" + +#: ../calendar/gui/dialogs/changed-comp.c:101 +#, c-format +msgid "%s You have made changes. Forget those changes and update the editor?" +msgstr "" +"%s Bạn đã tạo ra vài thay đổi. Bỏ qua những thay đổi này và cập nhật bộ " +"biên soạn không?" + +#: ../calendar/gui/dialogs/changed-comp.c:103 +#, c-format +msgid "%s You have made no changes, update the editor?" +msgstr "%s Bạn chưa thay đổi gì, còn cập nhật bộ biên soạn không?" + +#: ../calendar/gui/dialogs/comp-editor-page.c:464 +#, c-format +msgid "Validation error: %s" +msgstr "Lỗi hợp lệ hóa: %s" + +#: ../calendar/gui/dialogs/comp-editor-util.c:187 ../calendar/gui/print.c:2270 +#: ../calendar/gui/print.c:2261 ogginfo/ogginfo2.c:365 +msgid " to " +msgstr " tới " + +#: ../calendar/gui/dialogs/comp-editor-util.c:191 ../calendar/gui/print.c:2274 +#: ../calendar/gui/print.c:2265 +msgid " (Completed " +msgstr " (Đã hoàn tất " + +#: ../calendar/gui/dialogs/comp-editor-util.c:193 ../calendar/gui/print.c:2276 +#: ../calendar/gui/print.c:2267 +msgid "Completed " +msgstr "Đã hoàn tất " + +#: ../calendar/gui/dialogs/comp-editor-util.c:198 ../calendar/gui/print.c:2281 +#: ../calendar/gui/print.c:2272 +msgid " (Due " +msgstr " (Đến hạn " + +#: ../calendar/gui/dialogs/comp-editor-util.c:200 ../calendar/gui/print.c:2283 +#: ../calendar/gui/print.c:2274 +msgid "Due " +msgstr "Đến hạn " + +#: ../calendar/gui/dialogs/comp-editor.c:195 ../composer/e-msg-composer.c:2825 +#: ../calendar/gui/dialogs/comp-editor.c:190 ../composer/e-msg-composer.c:2663 +#, c-format +msgid "Attached message - %s" +msgstr "Thư đính kèm - %s" + +#: ../composer/e-msg-composer.c:2845 +#, c-format +msgid "Attached message" +msgid_plural "%d attached messages" +msgstr[0] "%d thư đính kèm" + +#: ../plug-ins/common/iwarp.c:1110 +msgid "_Move" +msgstr "_Chuyển" + +#: ../mail/em-folder-tree.c:977 ../mail/message-list.c:1711 +msgid "Cancel _Drag" +msgstr "Hủy bỏ _kéo" + +#: ../calendar/gui/dialogs/comp-editor.c:782 +#: ../calendar/gui/dialogs/comp-editor.c:771 +msgid "Could not update object" +msgstr "Không thể cập nhật đối tượng" + +#: ../calendar/gui/dialogs/comp-editor.c:971 ../composer/e-msg-composer.c:2317 +#, c-format +msgid "%d Attachment" +msgid_plural "%d Attachment" +msgstr[0] "%d đính kèm" + +#: ../calendar/gui/dialogs/comp-editor.c:958 +msgid "Hide Attachment _Bar" +msgstr "Ẩn thanh đính _kèm" + +#: ../calendar/gui/dialogs/comp-editor.c:1401 +msgid "Show Attachment _Bar" +msgstr "Hiện thanh đính _kèm" + +#: ../app/actions/layers-actions.c:56 ../src/lib/FeedListView.py:116 +msgid "_Properties" +msgstr "Th_uộc tính" + +#: ../composer/e-msg-composer.c:3401 +msgid "_Add attachment..." +msgstr "_Thêm đính kèm..." + +#: ../calendar/gui/dialogs/comp-editor.c:1425 +msgid "Attachment Button: Press space key to toggle attachment bar" +msgstr "Nút đính kèm: bấm phím dài để bật/tắt thanh đính kèm" + +#: ../calendar/gui/dialogs/comp-editor.c:1899 +msgid "Edit Appointment" +msgstr "Sửa đổi cuộc hẹn" + +#: ../calendar/gui/dialogs/comp-editor.c:1905 +#, c-format +msgid "Meeting - %s" +msgstr "Cuộc họp - %s" + +#: ../calendar/gui/dialogs/comp-editor.c:1907 +#, c-format +msgid "Appointment - %s" +msgstr "Cuộc hẹn - %s" + +#: ../calendar/gui/dialogs/comp-editor.c:1911 +#, c-format +msgid "Assigned Task - %s" +msgstr "Tác vụ đã gán - %s" + +#: ../calendar/gui/dialogs/comp-editor.c:1913 +#, c-format +msgid "Task - %s" +msgstr "Tác vụ - %s" + +#: ../calendar/gui/dialogs/comp-editor.c:1916 +#, c-format +msgid "Journal entry - %s" +msgstr "Mục nhật ký - %s" + +#: ../calendar/gui/dialogs/comp-editor.c:1926 +msgid "No summary" +msgstr "Không có tóm tắt" + +#: ../mail/em-utils.c:481 ../widgets/misc/e-attachment-bar.c:340 +msgid "attachment" +msgstr "đính kèm" + +#: ../calendar/gui/dialogs/comp-editor.c:2593 +msgid "Changes made to this item may be discarded if an update arrives" +msgstr "Thay đổi trên mục này có thể bị hủy nếu bản cập nhật đến vào." + +#: ../calendar/gui/dialogs/comp-editor.c:2782 +#: ../calendar/gui/dialogs/comp-editor.c:2617 +msgid "Unable to use current version!" +msgstr "• Không dùng được phiên bản hiện thời. •" + +#: ../calendar/gui/dialogs/copy-source-dialog.c:61 +msgid "Could not open source" +msgstr "Không thể mở nguồn." + +#: ../calendar/gui/dialogs/copy-source-dialog.c:69 +msgid "Could not open destination" +msgstr "Không thể mở đích." + +#: ../calendar/gui/dialogs/copy-source-dialog.c:78 +msgid "Destination is read only" +msgstr "Đích chỉ cho phép đọc thôi." + +#: ../calendar/gui/dialogs/delete-error.c:54 +msgid "The event could not be deleted due to a corba error" +msgstr "Không thể xoá bỏ sự kiện này vi găp lỗi kiểu CORBA." + +#: ../calendar/gui/dialogs/delete-error.c:57 +msgid "The task could not be deleted due to a corba error" +msgstr "Không thể xoá bỏ tác vụ này vi găp lỗi kiểu CORBA." + +#: ../calendar/gui/dialogs/delete-error.c:60 +msgid "The journal entry could not be deleted due to a corba error" +msgstr "Không thể xoá bỏ mục nhật ký này vi găp lỗi kiểu CORBA." + +#: ../calendar/gui/dialogs/delete-error.c:63 +msgid "The item could not be deleted due to a corba error" +msgstr "Không thể xoá bỏ mục này vi găp lỗi kiểu CORBA." + +#: ../calendar/gui/dialogs/delete-error.c:70 +msgid "The event could not be deleted because permission was denied" +msgstr "Không thể xoá bỏ sự kiện vì không đủ quyền." + +#: ../calendar/gui/dialogs/delete-error.c:73 +msgid "The task could not be deleted because permission was denied" +msgstr "Không thể xoá bỏ tác vụ vì không đủ quyền." + +#: ../calendar/gui/dialogs/delete-error.c:76 +msgid "The journal entry could not be deleted because permission was denied" +msgstr "Không thể xoá bỏ mục nhật ký vì không đủ quyền." + +#: ../calendar/gui/dialogs/delete-error.c:79 +msgid "The item could not be deleted because permission was denied" +msgstr "Không thể xoá bỏ mục vì không đủ quyền." + +#: ../calendar/gui/dialogs/delete-error.c:86 +msgid "The event could not be deleted due to an error" +msgstr "Không thể xoá bỏ sự kiện vì gặp lỗi." + +#: ../calendar/gui/dialogs/delete-error.c:89 +msgid "The task could not be deleted due to an error" +msgstr "Không thể xoá bỏ tác vụ vì gặp lỗi." + +#: ../calendar/gui/dialogs/delete-error.c:92 +msgid "The journal entry could not be deleted due to an error" +msgstr "Không thể xoá bỏ mục nhật ký vì gặp lỗi." + +#: ../calendar/gui/dialogs/delete-error.c:95 +msgid "The item could not be deleted due to an error" +msgstr "Không thể xoá bỏ mục vì gặp lỗi." + +#: ../calendar/gui/dialogs/e-delegate-dialog.glade.h:1 +msgid "Contacts..." +msgstr "Liên lạc..." + +#: ../storage/exchange-delegates.c:419 +msgid "Delegate To:" +msgstr "Ủy nhiệm cho:" + +#: ../calendar/gui/dialogs/e-delegate-dialog.glade.h:3 +msgid "Enter Delegate" +msgstr "Nhập người ủy nhiệm" + +#: ../calendar/gui/dialogs/event-editor.c:531 +msgid "Appoint_ment" +msgstr "Cuộc _hẹn" + +#: ../gncal/calendar-editor.glade.h:23 +msgid "Recurrence" +msgstr "Định kỳ" + +#: ../calendar/gui/dialogs/event-page.c:876 +#: ../calendar/gui/dialogs/task-page.c:444 +msgid "Or_ganizer" +msgstr "_Bộ tổ chức:" + +#: ../calendar/gui/dialogs/event-page.c:914 +msgid "_Delegatees" +msgstr "Người được ủ_y nhiệm" + +#: ../calendar/gui/dialogs/event-page.c:916 +msgid "Atte_ndees" +msgstr "Người _dự" + +#: ../calendar/gui/dialogs/event-page.c:1073 +#: ../calendar/gui/dialogs/event-page.c:729 +msgid "Event with no start date" +msgstr "Sự kiện không có ngày bắt đầu" + +#: ../calendar/gui/dialogs/event-page.c:1076 +#: ../calendar/gui/dialogs/event-page.c:732 +msgid "Event with no end date" +msgstr "Sự kiện không có ngày kết thúc" + +#: ../calendar/gui/dialogs/task-page.c:556 +msgid "Start date is wrong" +msgstr "Ngày bắt đầu sai" + +#: ../calendar/gui/dialogs/event-page.c:1252 +#: ../calendar/gui/dialogs/event-page.c:908 +msgid "End date is wrong" +msgstr "Ngày kết thúc sai" + +#: ../calendar/gui/dialogs/event-page.c:1275 +#: ../calendar/gui/dialogs/event-page.c:931 +msgid "Start time is wrong" +msgstr "Thời điểm đầu sai" + +#: ../calendar/gui/dialogs/event-page.c:1282 +#: ../calendar/gui/dialogs/event-page.c:938 +msgid "End time is wrong" +msgstr "Thời điểm kết thúc sai" + +#: ../calendar/gui/dialogs/meeting-page.c:463 +msgid "The organizer selected no longer has an account." +msgstr "Bộ tổ chức được chọn không còn có tài khoản." + +#: ../calendar/gui/dialogs/meeting-page.c:469 +msgid "An organizer is required." +msgstr "Cần một bộ tổ chức." + +#: ../calendar/gui/dialogs/meeting-page.c:484 +msgid "At least one attendee is required." +msgstr "Cần ít nhất một người dự." + +#: ../calendar/gui/dialogs/task-page.c:1184 +msgid "_Add " +msgstr "Th_êm " + +#: ../calendar/gui/dialogs/event-page.c:2553 +#: ../calendar/gui/dialogs/event-page.c:1684 +#, c-format +msgid "Unable to open the calendar '%s'." +msgstr "Không thể mở lịch « %s »." + +#: ../calendar/gui/dialogs/event-page.c:2808 +#: ../calendar/gui/dialogs/event-page.c:1890 +#, c-format +msgid "%d day before appointment" +msgid_plural "%d day before appointment" +msgstr[0] "%d ngày trước cuộc hẹn" + +#: ../calendar/gui/dialogs/event-page.c:2816 +#: ../calendar/gui/dialogs/event-page.c:1898 +#, c-format +msgid "%d hour before appointment" +msgid_plural "%d hour before appointment" +msgstr[0] "%d giờ trước cuộc hẹn" + +#: ../calendar/gui/dialogs/event-page.c:2824 +#: ../calendar/gui/dialogs/event-page.c:1906 +#, c-format +msgid "%d minute before appointement" +msgid_plural "%d minute before appointement" +msgstr[0] "%d phút trước cuộc hẹn" + +#: ../calendar/gui/dialogs/event-page.glade.h:1 +#: ../calendar/gui/dialogs/event-page.glade.h:3 +msgid "1 hour before appointment" +msgstr "1 giờ trước cuộc hẹn" + +#: ../calendar/gui/dialogs/event-page.glade.h:2 +#: ../calendar/gui/dialogs/event-page.glade.h:4 +msgid "15 minutes before appointment" +msgstr "15 phút trước cuộc hẹn" + +#: ../calendar/gui/dialogs/event-page.glade.h:3 +msgid "1day before appointment" +msgstr "1 ngày trước cuộc hẹn" + +#: ../calendar/gui/dialogs/event-page.glade.h:5 +#: ../calendar/gui/dialogs/task-page.glade.h:1 +msgid "Atte_ndees..." +msgstr "Người _dự..." + +#: ../calendar/gui/dialogs/event-page.glade.h:6 +msgid "C_ustomize" +msgstr "Tù_y chỉnh" + +#: ../calendar/gui/dialogs/task-page.glade.h:5 +msgid "Ca_tegories..." +msgstr "_Loại.." + +#: ../capplets/about-me/gnome-about-me.glade.h:18 +msgid "Cale_ndar:" +msgstr "_Lịch:" + +#: ../calendar/gui/dialogs/event-page.glade.h:10 +#: ../calendar/gui/dialogs/event-page.glade.h:15 +msgid "Event Description" +msgstr "Mô tả sự kiện" + +#: ../calendar/gui/dialogs/event-page.glade.h:11 +#: ../calendar/gui/dialogs/event-page.glade.h:16 +msgid "Locat_ion:" +msgstr "_Địa điểm:" + +#: ../calendar/gui/dialogs/event-page.glade.h:12 +#: ../calendar/gui/dialogs/meeting-page.glade.h:5 +msgid "Or_ganizer:" +msgstr "_Bộ tổ chức:" + +#: ../calendar/gui/dialogs/task-page.glade.h:13 +msgid "Su_mmary:" +msgstr "Tó_m tắt:" + +#: ../data/glade/new-property.glade.h:3 ../plug-ins/metadata/interface.c:301 +#: ../src/drivel.glade.h:67 ../ui/muds.glade.h:49 ../src/gtkfunc.c:619 +msgid "_Description:" +msgstr "_Mô tả:" + +#: ../calendar/gui/dialogs/event-page.glade.h:16 +msgid "_Set alarm\t" +msgstr "_Lặp lại báo động\t" + +#: ../calendar/gui/dialogs/event-page.glade.h:17 +msgid "_Time:" +msgstr "_Giờ :" + +#: ../new.php:28 +msgid "for" +msgstr "cho" + +#: ../calendar/gui/dialogs/event-page.glade.h:21 +#: ../calendar/gui/dialogs/recurrence-page.glade.h:11 +msgid "until" +msgstr "tới khi" + +#: ../calendar/gui/dialogs/meeting-page.c:306 +#: ../calendar/gui/dialogs/meeting-page.c:303 +msgid "Dele_gatees" +msgstr "Người được ủ_y nhiệm" + +#: ../trashapplet/trashapplet.glade.h:1 +msgid "From:" +msgstr "Từ :" + +#: ../calendar/gui/e-meeting-list-view.c:358 +msgid "Attendee" +msgstr "Người dự" + +#: ../calendar/gui/dialogs/meeting-page.etspec.h:2 +#: ../calendar/gui/e-meeting-time-sel.etspec.h:2 +msgid "Click here to add an attendee" +msgstr "Nhấn vào đây để thêm người dự" + +#: ../calendar/gui/dialogs/meeting-page.etspec.h:3 +#: ../calendar/gui/e-meeting-time-sel.etspec.h:3 +msgid "Common Name" +msgstr "Tên chung" + +#: ../calendar/gui/dialogs/meeting-page.etspec.h:4 +#: ../calendar/gui/e-meeting-time-sel.etspec.h:4 +msgid "Delegated From" +msgstr "Được ủy nhiệm từ" + +#: ../calendar/gui/dialogs/meeting-page.etspec.h:5 +#: ../calendar/gui/e-meeting-time-sel.etspec.h:5 +msgid "Delegated To" +msgstr "Đã ủy nhiệm cho" + +#: ../providers/ibmdb2/gda-ibmdb2-provider.c:864 +msgid "Language" +msgstr "Ngôn ngữ" + +#: ../gnome-netinfo/netstat.c:687 ../src/lib/FeedPropertiesDialog.py:92 +msgid "Member" +msgstr "Thành viên" + +#: ../calendar/gui/e-meeting-list-view.c:386 +msgid "RSVP" +msgstr "Trả lời trước" + +#. #-#-#-#-# gpsdrive-2.08pre6.vi.po (gpsdrive-2.08pre6) #-#-#-#-# +#. gdk_window_lower((GdkWindow *)menuwin2); +#: ../ui/prefs.glade.h:35 ../src/session.c:2024 ../pan/task-manager.c:708 +#: ../storage/sunone-invitation-list.c:534 src/gpsdrive.c:11766 +#: src/gpsdrive.c:11809 ../mimedir/mimedir-vcomponent.c:301 +msgid "Status" +msgstr "Trạng thái" + +#: ../calendar/gui/dialogs/meeting-page.glade.h:1 +msgid "Att_endees" +msgstr "Người _dự" + +#: ../calendar/gui/dialogs/meeting-page.glade.h:2 +msgid "C_hange Organizer" +msgstr "Đổ_i bộ tổ chức" + +#: ../calendar/gui/dialogs/meeting-page.glade.h:3 +msgid "Co_ntacts..." +msgstr "_Liên lạc..." + +#: ../calendar/gui/dialogs/meeting-page.glade.h:6 +#: ../storage/sunone-invitation-list.c:512 +msgid "Organizer" +msgstr "Bộ tổ chức:" + +#: ../calendar/gui/dialogs/meeting-page.glade.h:7 +#: ../calendar/gui/e-itip-control.glade.h:9 +msgid "Organizer:" +msgstr "Tổ chức:" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. One textfield per split that should help you remember what this split was about. +#: ../calendar/gui/dialogs/memo-editor.c:130 ../calendar/gui/print.c:2311 +#: prefs_gui.c:374 +msgid "Memo" +msgstr "Ghi nhớ" + +#: ../calendar/gui/dialogs/memo-page.c:490 +#, c-format +msgid "Unable to open memos in '%s'." +msgstr "Không thể mở ghi nhớ trong « %s »." + +#: ../calendar/gui/dialogs/memo-page.glade.h:2 +msgid "Basics" +msgstr "Cơ bản" + +#: ../calendar/gui/dialogs/task-page.glade.h:7 +msgid "Classi_fication:" +msgstr "_Phân loại:" + +#: ../providers/evolution/gda-calendar-model.c:361 +msgid "Confidential" +msgstr "Tin tưởng" + +#: ../src/dialogs.c:627 ../src/main.c:344 ../src/utils.c:574 +#: address_gui.c:2755 datebook_gui.c:4461 memo_gui.c:1609 todo_gui.c:2271 +#: po/silky-channel.glade.h:14 +msgid "Private" +msgstr "Riêng" + +#: ../src/dialogs.c:622 ../src/main.c:340 ../src/utils.c:580 +msgid "Public" +msgstr "Công" + +#: ../data/glade/resource-input-dialog.glade.h:5 +msgid "_Group:" +msgstr "_Nhóm:" + +#: ../calendar/gui/dialogs/memo-page.glade.h:9 +msgid "_Memo Content:" +msgstr "Nội dung ghi _nhớ :" + +#: ../calendar/gui/dialogs/new-calendar.glade.h:2 +msgid "Calendar options" +msgstr "Tùy chọn lịch" + +#: ../calendar/gui/dialogs/new-calendar.glade.h:3 +msgid "Add New Calendar" +msgstr "Thêm lịch mới" + +#: ../calendar/gui/dialogs/new-calendar.glade.h:4 +msgid "Calendar Group" +msgstr "Nhóm lịch" + +#: ../calendar/gui/dialogs/new-calendar.glade.h:5 +msgid "Calendar Location" +msgstr "Địa điểm lịch" + +#: ../calendar/gui/dialogs/new-calendar.glade.h:6 +msgid "Calendar Name" +msgstr "Tên lịch" + +#: ../calendar/gui/dialogs/new-task-list.glade.h:2 +msgid "Task List Options" +msgstr "Tùy chọn Danh sách Tác vụ" + +#: ../calendar/gui/dialogs/new-task-list.glade.h:3 +msgid "Add New Task List" +msgstr "Thêm Danh sách Tác vụ mới" + +#: ../calendar/gui/dialogs/new-task-list.glade.h:4 +msgid "Task List Group" +msgstr "Nhóm Danh sách Tác vụ" + +#: ../calendar/gui/dialogs/new-task-list.glade.h:5 +msgid "Task List Name" +msgstr "Tên Danh sách Tác vụ" + +#: ../calendar/gui/dialogs/recur-comp.c:52 +msgid "You are modifying a recurring event. What would you like to modify?" +msgstr "Bạn đang sửa đổi sự kiện lặp, bạn muốn sửa đổi cái nào?" + +#: ../calendar/gui/dialogs/recur-comp.c:54 +msgid "You are delegating a recurring event. What would you like to delegate?" +msgstr "Bạn đang ủy nhiệm sự kiện lặp, bạn muốn ủy nhiệm cái nào?" + +#: ../calendar/gui/dialogs/recur-comp.c:58 +msgid "You are modifying a recurring task. What would you like to modify?" +msgstr "Bạn đang sửa đổi tác vụ lặp, bạn muốn sửa đổi cái nào?" + +#: ../calendar/gui/dialogs/recur-comp.c:62 +msgid "" +"You are modifying a recurring journal entry. What would you like to modify?" +msgstr "Bạn đang sửa đổi mục nhật ký lặp, bạn muốn sửa đổi cái nào?" + +#: ../calendar/gui/dialogs/recur-comp.c:90 +msgid "This Instance Only" +msgstr "Chỉ lần này" + +#: ../calendar/gui/dialogs/recur-comp.c:94 +msgid "This and Prior Instances" +msgstr "Lần này và những lần trước" + +#: ../calendar/gui/dialogs/recur-comp.c:100 +msgid "This and Future Instances" +msgstr "Lần này và những lần sau này" + +#: ../calendar/gui/dialogs/recur-comp.c:105 +msgid "All Instances" +msgstr "Mọi lần" + +#: ../calendar/gui/dialogs/recurrence-page.c:498 +#: ../calendar/gui/dialogs/recurrence-page.c:494 +msgid "This appointment contains recurrences that Evolution cannot edit." +msgstr "Cuộc hẹn chứa nhiều lần lặp lại mà Evolution không thể hiệu chỉnh." + +#: ../calendar/gui/dialogs/recurrence-page.c:819 +#: ../calendar/gui/dialogs/recurrence-page.c:815 +msgid "Recurrence date is invalid" +msgstr "Ngày lặp không hợp lệ" + +#: makeinfo/defun.c:509 makeinfo/defun.c:513 makeinfo/defun.c:517 +#: makeinfo/defun.c:551 makeinfo/defun.c:650 makeinfo/xml.c:2234 +#: libexif/canon/mnote-canon-entry.c:78 +#, fuzzy +msgid "on" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"trên\n" +"#-#-#-#-# libexif-0.6.13.vi.po (libexif-0.6.13) #-#-#-#-#\n" +"bật" + +#: ../calendar/gui/dialogs/recurrence-page.c:990 event-ui.c:991 +#: ../calendar/gui/dialogs/recurrence-page.c:986 +msgid "first" +msgstr "thứ nhắt" + +#: ../calendar/gui/dialogs/recurrence-page.c:992 event-ui.c:995 +#: ../calendar/gui/dialogs/recurrence-page.c:988 +msgid "third" +msgstr "thứ ba" + +#: ../calendar/gui/dialogs/recurrence-page.c:993 event-ui.c:997 +#: ../calendar/gui/dialogs/recurrence-page.c:989 +msgid "fourth" +msgstr "thứ tư" + +#: ../calendar/gui/dialogs/recurrence-page.c:994 event-ui.c:1001 +#: ../calendar/gui/dialogs/recurrence-page.c:990 +msgid "last" +msgstr "cuối cùng" + +#: ../calendar/gui/dialogs/recurrence-page.c:1017 +#: ../calendar/gui/dialogs/recurrence-page.c:1013 +msgid "Other Date" +msgstr "Ngày khác" + +#: ../src/crontab.py:248 datebook_gui.c:1824 ../bin/ical-dump.c:87 +msgid "day" +msgstr "ngày" + +#: ../calendar/gui/dialogs/recurrence-page.c:1182 event-ui.c:983 +#: ../calendar/gui/dialogs/recurrence-page.c:1178 +msgid "on the" +msgstr "vào" + +#: ../gncal/calendar-editor.glade.h:40 +msgid "occurrences" +msgstr "lần" + +#: ../calendar/gui/dialogs/recurrence-page.c:2326 src/callerid.c:426 +#: ../calendar/gui/dialogs/recurrence-page.c:2323 +msgid "Date/Time" +msgstr "Ngày/Giờ" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:1 +#: ../gncal/calendar-editor.glade.h:4 +msgid "Exceptions" +msgstr "Ngoại lệ" + +#: ../resources/dialogexport.glade.h:3 +msgid "Preview" +msgstr "Xem thử" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:3 +msgid "Recurrence" +msgstr "Định kỳ" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:4 event-ui.c:898 +#: event-ui.c:920 event-ui.c:963 event-ui.c:1018 +msgid "Every" +msgstr "Mỗi" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:5 +msgid "This appointment rec_urs" +msgstr "Cuộc hẹn này _lặp lại" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:9 event-ui.c:1053 +msgid "forever" +msgstr "mãi mãi" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:10 +msgid "month(s)" +msgstr "tháng" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:12 +msgid "week(s)" +msgstr "tuần" + +#: ../calendar/gui/dialogs/recurrence-page.glade.h:13 +msgid "year(s)" +msgstr "năm" + +#: ../calendar/gui/dialogs/task-details-page.c:430 +msgid "Completed date is wrong" +msgstr "Ngày hoàn tất sai" + +#: ../calendar/gui/dialogs/task-details-page.c:522 +msgid "Web Page" +msgstr "Trang Mạng" + +#: ../calendar/gui/dialogs/task-details-page.glade.h:1 +msgid "Miscellaneous" +msgstr "Linh tinh\t" + +#: ../calendar/gui/dialogs/task-details-page.glade.h:2 +msgid "Status" +msgstr "Trạng thái" + +#: ../calendar/gui/print.c:2372 ../plugins/save-calendar/csv-format.c:390 +#: todo-ui.c:299 ../todo-ui.c:156 todo_gui.c:2223 +msgid "Completed" +msgstr "Hoàn tất" + +#: ../todo-ui.c:163 web/template/resources_edit_main_anon.tpl:92 +#: web/template/resources_edit_main.tpl:91 ../pan/filter-edit-ui.c:855 +#: libexif/pentax/mnote-pentax-entry.c:99 +#: libexif/pentax/mnote-pentax-entry.c:104 +msgid "High" +msgstr "Cao" + +#: ../calendar/gui/e-calendar-table.c:454 ../calendar/gui/print.c:2358 +msgid "In Progress" +msgstr "Đang tiến hành" + +#: ../todo-ui.c:165 web/template/resources_edit_main_anon.tpl:90 +#: web/template/resources_edit_main.tpl:89 ../pan/filter-edit-ui.c:858 +#: libexif/pentax/mnote-pentax-entry.c:98 +#: libexif/pentax/mnote-pentax-entry.c:103 +msgid "Low" +msgstr "Thấp" + +#: ../src/gwget_data.c:231 +msgid "Not Started" +msgstr "Chưa bắt đầu" + +#: ../calendar/gui/dialogs/task-details-page.glade.h:10 +msgid "P_ercent complete:" +msgstr "_Phần trăm hoàn tất:" + +#: ../calendar/gui/dialogs/task-details-page.glade.h:11 +msgid "Stat_us:" +msgstr "Trạn_g thái:" + +#: ../app/widgets/gimppaletteeditor.c:695 ../objects/UML/class_dialog.c:2217 +#: ../objects/UML/umlparameter.c:34 libexif/exif-format.c:44 +msgid "Undefined" +msgstr "Chưa định nghĩa" + +#: ../calendar/gui/dialogs/task-details-page.glade.h:13 +#: ../calendar/gui/dialogs/task-details-page.glade.h:12 +msgid "_Date completed:" +msgstr "Ngày hoàn _tất:" + +#: ../data/glade/task-dialog.glade.h:17 ../gncal/todo-dialog.c:481 +msgid "_Priority:" +msgstr "_Ưu tiên:" + +#: ../calendar/gui/dialogs/task-details-page.glade.h:15 +msgid "_Web Page:" +msgstr "Trang _Mạng:" + +#: ../calendar/gui/tasks-component.c:1165 ../src/gnome-schedule.glade.h:70 +msgid "_Task" +msgstr "_Tác vụ" + +#: ../calendar/gui/dialogs/task-editor.c:423 +msgid "Task Details" +msgstr "Chi tiết tác vụ" + +#: ../calendar/gui/dialogs/task-page.c:437 +msgid "_Group" +msgstr "_Nhóm" + +#: ../calendar/gui/dialogs/task-page.c:821 +#: ../calendar/gui/dialogs/task-page.c:529 +msgid "Due date is wrong" +msgstr "Ngày đến hạn sai" + +#: ../calendar/gui/dialogs/task-page.c:1614 +#: ../calendar/gui/dialogs/task-page.c:873 +#, c-format +msgid "Unable to open tasks in '%s'." +msgstr "Không thể mở tác vụ trong « %s »." + +#: ../calendar/gui/dialogs/task-page.glade.h:2 +msgid "Categor_ies..." +msgstr "_Loại.." + +#: ../calendar/gui/dialogs/task-page.glade.h:3 +#: ../calendar/gui/dialogs/task-page.glade.h:9 +msgid "D_escription:" +msgstr "_Mô tả:" + +#: ../calendar/gui/dialogs/task-page.glade.h:4 +msgid "Or_ganiser:" +msgstr "_Bộ tổ chức:" + +#: ../calendar/gui/dialogs/task-page.glade.h:5 +#: ../calendar/gui/dialogs/task-page.glade.h:12 +msgid "Sta_rt date:" +msgstr "Ngày _bắt đầu :" + +#: ../calendar/gui/dialogs/task-page.glade.h:7 +#: ../interfaces/time.glade.in.h:11 +msgid "Time zone:" +msgstr "Múi giờ :" + +#: ../calendar/gui/dialogs/task-page.glade.h:8 +#: ../calendar/gui/dialogs/task-page.glade.h:15 +msgid "_Due date:" +msgstr "N_gày đến hạn:" + +#: ../calendar/gui/dialogs/url-editor-dialog.glade.h:3 +msgid "Free/Busy C_alendars" +msgstr "_Lịch Rảnh/Bận" + +#: ../calendar/gui/dialogs/url-editor-dialog.glade.h:4 +msgid "Publishing Frequency" +msgstr "Tần số Xuất bản" + +#: ../calendar/gui/dialogs/url-editor-dialog.glade.h:5 +msgid "Publishing _Location" +msgstr "Đị_a điểm Xuất bản" + +#: ../calendar/gui/dialogs/url-editor-dialog.glade.h:6 +msgid "Free/Busy Publishing Settings" +msgstr "Thiết lập Xuất bản Rảnh/Bận" + +#: ../calendar/gui/dialogs/url-editor-dialog.glade.h:7 +msgid "_Daily" +msgstr "_Hàng ngày" + +#: ../src/gnome-schedule.glade.h:67 +#, fuzzy +msgid "_Manual" +msgstr "" +"#-#-#-#-# compendium4ALL.po (atomix HEAD) #-#-#-#-#\n" +"_Thủ công\n" +"#-#-#-#-# vi.po (gnome-schedule Gnome HEAD) #-#-#-#-#\n" +"_Sổ tay" + +#: ../calendar/gui/dialogs/url-editor-dialog.glade.h:12 +msgid "_Weekly" +msgstr "_Hàng tuần" + +#: ../calendar/gui/e-alarm-list.c:395 ../src/gnome-torrent.in:241 +#, c-format +msgid "%d day" +msgid_plural "%d day" +msgstr[0] "%d ngày" + +#: ../calendar/gui/e-alarm-list.c:400 +#, c-format +msgid "%d week" +msgid_plural "%d week" +msgstr[0] "%d tuần" + +#: ../calendar/gui/e-alarm-list.c:462 +msgid "Unknown action to be performed" +msgstr "Không biết hành động cần thực hiện." + +#. Translator: The first %s refers to the base, which would be actions like +#. * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" +#: ../calendar/gui/e-alarm-list.c:476 +#, c-format +msgid "%s %s before the start of the appointment" +msgstr "%s %s trước khi bắt đầu cuộc hẹn" + +#. Translator: The first %s refers to the base, which would be actions like +#. * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" +#: ../calendar/gui/e-alarm-list.c:481 +#, c-format +msgid "%s %s after the start of the appointment" +msgstr "%s %s sau khi bắt đầu cuộc hẹn" + +#. Translator: The %s refers to the base, which would be actions like +#. * "Play a sound" +#: ../calendar/gui/e-alarm-list.c:488 +#, c-format +msgid "%s at the start of the appointment" +msgstr "%s lúc bắt đầu cuộc hẹn" + +#: ../calendar/gui/e-alarm-list.c:497 +#, c-format +msgid "%s %s before the end of the appointment" +msgstr "%s %s trước khi kết thúc cuộc hẹn" + +#: ../calendar/gui/e-alarm-list.c:500 +#, c-format +msgid "%s %s after the end of the appointment" +msgstr "%s %s sau khi kết thúc cuộc hẹn" + +#: ../calendar/gui/e-alarm-list.c:505 +#, c-format +msgid "%s at the end of the appointment" +msgstr "%s lúc kết thúc cuộc hẹn" + +#: ../calendar/gui/e-alarm-list.c:527 +#, c-format +msgid "%s at %s" +msgstr "%s lúc %s" + +#: ../calendar/gui/e-alarm-list.c:533 +#, c-format +msgid "%s for an unknown trigger type" +msgstr "%s cho loại gây ra lạ" + +#: ../mail/em-folder-view.c:2703 +#, c-format +msgid "Click to open %s" +msgstr "Nhấn để mở %s" + +#: ../calendar/gui/e-cal-component-memo-preview.c:201 +msgid "Memo:" +msgstr "Ghi nhớ :" + +#: ../calendar/gui/e-cal-component-preview.c:299 +msgid "Web Page:" +msgstr "Trang Mạng:" + +#: makeinfo/html.c:207 ../src/utils.c:597 ../src/utils.c:644 +msgid "Untitled" +msgstr "Không tên" + +#: ../calendar/gui/e-itip-control.c:1108 ../todo-ui.c:272 +msgid "Summary:" +msgstr "Tóm tắt:" + +#: ../calendar/gui/e-cal-component-preview.c:204 +msgid "Start Date:" +msgstr "Ngày bắt đầu :" + +#: ../calendar/gui/e-cal-component-preview.c:217 +#: ../calendar/gui/e-cal-component-preview.c:215 +msgid "Due Date:" +msgstr "Ngày đến hạn:" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. Status +#: ../src/Dialog_Partition_Info.cc:184 ../todo-ui.c:269 +#: ../glade/gnome/gnomeappbar.c:71 ../ui/user_info.glade.h:62 +#: ../storage/sunone-itip-view.c:752 +msgid "Status:" +msgstr "Trạng thái:" + +#: ../todo-ui.c:267 ../lib/gtkorphan_ops.pm:190 +msgid "Priority:" +msgstr "Ưu tiên:" + +#: ../calendar/gui/e-cal-list-view.etspec.h:2 +msgid "End Date" +msgstr "Ngày kết thúc" + +#: ../calendar/gui/e-cal-list-view.etspec.h:4 +msgid "Start Date" +msgstr "Ngày bắt đầu" + +#: ../gncal/gnomecal-prefs.c:1311 ../gncal/todo-list.c:1051 +#: ../glom/data_structure/layout/report_parts/layoutitem_summary.cc:58 +#: ../storage/sunone-invitation-list.c:503 ../mimedir/mimedir-vcomponent.c:309 +msgid "Summary" +msgstr "Tóm tắt" + +#: ../calendar/gui/e-cal-model-calendar.c:183 +#: ../calendar/gui/e-calendar-table.c:440 ../src/interface.c:291 +#: ../objects/standard/box.c:137 ../objects/standard/ellipse.c:131 +#: ../widgets/gtk+.xml.in.h:80 +#, fuzzy +msgid "Free" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Rảnh\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Tự do" + +#: ../calendar/gui/e-meeting-time-sel.c:412 po/silky.glade.h:65 +msgid "Busy" +msgstr "Bận" + +#: ../calendar/gui/e-cal-model-tasks.c:607 +msgid "" +"The geographical position must be entered in the format: \n" +"\n" +"45.436845,125.862501" +msgstr "" +"Vị trí địa lý phải được nhập theo dạng thức:\n" +"\n" +"10.783114,106.699804 (Sài Gòn)" + +#: ../testing/html.c:293 ../src/glade-editor-property.c:1508 +#: ../src/glade-editor-property.c:1524 ../src/widgets/togglebutton.cc:45 +#: address_gui.c:698 datebook_gui.c:670 memo_gui.c:527 todo_gui.c:662 +#: utils.c:1274 ../hwconf.c:362 ../hwconf.c:501 libexif/exif-entry.c:487 +#: libexif/olympus/mnote-olympus-entry.c:135 +#: libexif/olympus/mnote-olympus-entry.c:159 app/gui-subs.c:492 +msgid "Yes" +msgstr "Có" + +#: ../testing/html.c:293 dir.c:1030 dir.c:1051 +#: ../src/glade-editor-property.c:1508 ../src/glade-editor-property.c:1524 +#: ../src/glade-editor-property.c:1539 ../src/widgets/togglebutton.cc:47 +#: address_gui.c:698 datebook_gui.c:670 memo_gui.c:527 todo_gui.c:662 +#: utils.c:1274 ../hwconf.c:362 ../hwconf.c:501 +#: libexif/olympus/mnote-olympus-entry.c:134 +#: libexif/olympus/mnote-olympus-entry.c:158 app/gui-subs.c:500 +msgid "No" +msgstr "Không" + +#: ../calendar/gui/e-cal-model.c:922 ../calendar/gui/e-cal-model.c:920 +msgid "Recurring" +msgstr "Lặp lại" + +#: ../calendar/gui/e-cal-model.c:924 ../calendar/gui/e-cal-model.c:922 +#: ../src/planner-task-dialog.c:2557 +msgid "Assigned" +msgstr "Đã gán" + +#: ../Pyblio/GnomeUI/Document.py:726 +msgid "Save As..." +msgstr "Lưu dạng..." + +#: ../calendar/gui/e-cal-popup.c:187 ../mail/em-format-html-display.c:1842 +#: ../mail/em-format-html-display.c:1791 +msgid "Select folder to save selected attachments..." +msgstr "Chọn thư mục nơi cần lưu những đính kèm đã chọn..." + +#: ../mail/em-popup.c:424 +#, c-format +msgid "untitled_image.%s" +msgstr "untitled_image.%s" + +#: ../extensions/page-info/page-info-dialog.c:1056 +#: ../extensions/page-info/page-info-dialog.c:1408 ../src/Actions.cs:55 +msgid "_Save As..." +msgstr "Lưu _dang..." + +#: ../calendar/gui/e-cal-popup.c:271 ../mail/em-popup.c:554 +#: ../mail/em-popup.c:565 ../mail/em-popup.c:542 ../mail/em-popup.c:553 +#: ../libnautilus-private/nautilus-dnd.c:655 ../src/f-spot.glade.h:133 +msgid "Set as _Background" +msgstr "Đặt làm _nền" + +#: ../calendar/gui/e-cal-popup.c:272 +msgid "_Save Selected" +msgstr "_Lưu các điều chọn" + +#: ../calendar/gui/e-cal-popup.c:394 ../mail/em-popup.c:774 +#: ../mail/em-popup.c:762 +#, c-format +msgid "Open in %s..." +msgstr "Mở bằng « %s »..." + +#: ../calendar/gui/e-calendar-table.c:410 +#: ../calendar/gui/e-calendar-table.c:402 ../src/gnome-netstatus.glade.h:4 +#, no-c-format +msgid "0%" +msgstr "0%" + +#: ../calendar/gui/e-calendar-table.c:411 +#: ../calendar/gui/e-calendar-table.c:403 +msgid "10%" +msgstr "10%" + +#: ../calendar/gui/e-calendar-table.c:412 +#: ../calendar/gui/e-calendar-table.c:404 +msgid "20%" +msgstr "20%" + +#: ../calendar/gui/e-calendar-table.c:413 +#: ../calendar/gui/e-calendar-table.c:405 +msgid "30%" +msgstr "30%" + +#: ../calendar/gui/e-calendar-table.c:414 +#: ../calendar/gui/e-calendar-table.c:406 +msgid "40%" +msgstr "40%" + +#: ../src/nautilus-file-management-properties.glade.h:20 +#, no-c-format +msgid "50%" +msgstr "50%" + +#: ../calendar/gui/e-calendar-table.c:416 +#: ../calendar/gui/e-calendar-table.c:408 +msgid "60%" +msgstr "60%" + +#: ../calendar/gui/e-calendar-table.c:417 +#: ../calendar/gui/e-calendar-table.c:409 +msgid "70%" +msgstr "70%" + +#: ../calendar/gui/e-calendar-table.c:418 +#: ../calendar/gui/e-calendar-table.c:410 +msgid "80%" +msgstr "80%" + +#: ../calendar/gui/e-calendar-table.c:419 +#: ../calendar/gui/e-calendar-table.c:411 +msgid "90%" +msgstr "90%" + +#: ../src/nautilus-file-management-properties.glade.h:8 +#, no-c-format +msgid "100%" +msgstr "100%" + +#: ../calendar/gui/e-calendar-table.c:527 +#: ../calendar/gui/e-calendar-table.c:514 +msgid "Task Table" +msgstr "Bảng tác vụ" + +#: ../calendar/gui/e-calendar-view.c:661 +msgid "Deleting selected objects" +msgstr "Đang xoá bỏ các đối tượng đã chọn..." + +#: ../calendar/gui/e-calendar-view.c:789 +msgid "Updating objects" +msgstr "Đang cập nhật các đối tượng..." + +#: ../calendar/gui/e-calendar-view.c:1105 ../composer/e-msg-composer.c:1242 +#: ../gedit/gedit-file.c:372 +msgid "Save as..." +msgstr "Lưu dạng..." + +#: ../calendar/gui/e-calendar-table.c:1177 ../calendar/gui/e-memo-table.c:855 +#: ../calendar/gui/e-calendar-table.c:1155 +msgid "Open _Web Page" +msgstr "Mở trang _Mạng" + +#: ../src/ghex-ui.xml.h:6 ui/galeon-bookmarks-editor-ui.xml.in.h:1 +#: app/menubar.c:449 app/menubar.c:460 app/menubar.c:516 app/menubar.c:547 +msgid "C_ut" +msgstr "Cắ_t" + +#: ../app/actions/edit-actions.c:101 app/menubar.c:453 app/menubar.c:464 +#: app/menubar.c:551 +msgid "_Paste" +msgstr "_Dán" + +#: ../calendar/gui/e-calendar-table.c:1189 ../ui/evolution-tasks.xml.h:22 +#: ../calendar/gui/e-calendar-table.c:1167 ../ui/evolution-tasks.xml.h:20 +msgid "_Assign Task" +msgstr "_Gán tác vụ" + +#: ../ui/evolution-tasks.xml.h:24 +msgid "_Forward as iCalendar" +msgstr "_Chuyển dạng iCalendar" + +#: ../calendar/gui/e-calendar-table.c:1191 +#: ../calendar/gui/e-calendar-table.c:1169 +msgid "_Mark as Complete" +msgstr "Đánh dấu _hoàn tất" + +#: ../calendar/gui/e-calendar-table.c:1192 +#: ../calendar/gui/e-calendar-table.c:1170 +msgid "_Mark Selected Tasks as Complete" +msgstr "Đánh _dấu các tác vụ đã chọn là hoàn tất" + +#: ../calendar/gui/e-calendar-table.c:1197 +#: ../calendar/gui/e-calendar-table.c:1175 +msgid "_Delete Selected Tasks" +msgstr "_Xoá bỏ các tác vụ đã chọn" + +#: ../calendar/gui/e-calendar-table.c:1326 +msgid "Click to add a task" +msgstr "Nhấn để thêm tác vụ" + +#: ../calendar/gui/e-calendar-table.etspec.h:2 +#, no-c-format +msgid "% Complete" +msgstr "% hoàn tất" + +#: ../calendar/gui/e-calendar-table.etspec.h:5 ../mail/mail-send-recv.c:617 +#: ../iagno/properties.c:644 ../nautilus-cd-burner.c:1144 +msgid "Complete" +msgstr "Hoàn tất" + +#: ../calendar/gui/e-calendar-table.etspec.h:6 +msgid "Completion date" +msgstr "Ngày hoàn tất" + +#: ../calendar/gui/e-calendar-table.etspec.h:7 +#: ../providers/evolution/gda-calendar-model.c:64 +msgid "Due date" +msgstr "Ngày đến hạn" + +#: src/pkg_columnizer.cc:95 dselect/pkgtop.cc:287 ../gncal/todo-list.c:1073 +#: ../src/mlview-validator-window.cc:678 ../pan/save-ui.c:268 +#: ../mimedir/mimedir-vcomponent.c:290 schroot/sbuild-chroot.cc:389 +msgid "Priority" +msgstr "Ưu tiên" + +#: ../calendar/gui/e-calendar-table.etspec.h:9 +#: ../providers/evolution/gda-calendar-model.c:66 +msgid "Start date" +msgstr "Ngày bắt đầu" + +#: ../calendar/gui/e-calendar-table.etspec.h:12 +msgid "Task sort" +msgstr "Sắp xếp tác vụ" + +#: ../calendar/gui/e-calendar-view.c:1267 +#: ../calendar/gui/e-calendar-view.c:1231 +msgid "Moving items" +msgstr "Đang di chuyển mục..." + +#: ../calendar/gui/e-calendar-view.c:1269 +#: ../calendar/gui/e-calendar-view.c:1233 +msgid "Copying items" +msgstr "Đang sao chép mục..." + +#: ../calendar/gui/e-calendar-view.c:1533 +#: ../calendar/gui/e-calendar-view.c:1504 +msgid "New _Appointment..." +msgstr "Cuộc _hẹn mới..." + +#: ../calendar/gui/e-calendar-view.c:1534 +#: ../calendar/gui/e-calendar-view.c:1505 +msgid "New All Day _Event" +msgstr "Sự _kiện nguyên ngày mới" + +#: ../calendar/gui/e-calendar-view.c:1535 +msgid "New _Meeting" +msgstr "Cuộc _họp mới" + +#: ../calendar/gui/e-calendar-view.c:1536 +msgid "New _Task" +msgstr "_Tác vụ mới" + +#. FIXME: hook in this somehow +#: ../calendar/gui/e-calendar-view.c:1546 +#: ../widgets/menus/gal-view-menus.c:291 +msgid "_Current View" +msgstr "_Khung xem hiện thời" + +#: ../calendar/gui/e-calendar-view.c:1548 +msgid "Select T_oday" +msgstr "Chọn _hôm nay" + +#: ../calendar/gui/e-calendar-view.c:1549 +#: ../calendar/gui/e-calendar-view.c:1520 +msgid "_Select Date..." +msgstr "_Chọn ngày..." + +#: ../calendar/gui/e-calendar-view.c:1565 +#: ../calendar/gui/e-calendar-view.c:1541 +msgid "Cop_y to Calendar..." +msgstr "_Chép vào lịch..." + +#: ../calendar/gui/e-calendar-view.c:1566 +#: ../calendar/gui/e-calendar-view.c:1542 +msgid "Mo_ve to Calendar..." +msgstr "_Chuyển vào lịch..." + +#: ../calendar/gui/e-calendar-view.c:1567 +#: ../calendar/gui/e-calendar-view.c:1543 +msgid "_Delegate Meeting..." +msgstr "Ủ_y nhiệm cuộc họp..." + +#: ../calendar/gui/e-calendar-view.c:1568 +#: ../calendar/gui/e-calendar-view.c:1544 +msgid "_Schedule Meeting..." +msgstr "Lập lịch _cuộc họp..." + +#: ../calendar/gui/e-calendar-view.c:1569 +#: ../calendar/gui/e-calendar-view.c:1545 +msgid "_Forward as iCalendar..." +msgstr "_Chuyển dạng iCalendar..." + +#: ../calendar/gui/e-calendar-view.c:1574 +#: ../calendar/gui/e-calendar-view.c:1550 +msgid "Make this Occurrence _Movable" +msgstr "Cho phép di chuyển _lần này" + +#: ../calendar/gui/e-calendar-view.c:1575 +#: ../calendar/gui/e-calendar-view.c:1551 +msgid "Delete this _Occurrence" +msgstr "_Xoá bỏ lần này" + +#: ../calendar/gui/e-calendar-view.c:1576 +#: ../calendar/gui/e-calendar-view.c:1552 +msgid "Delete _All Occurrences" +msgstr "Xoá bỏ _mọi lần" + +#. To Translators: It will display "Organiser: NameOfTheUser " +#: ../calendar/gui/e-calendar-view.c:1951 +#, c-format +msgid "Organizer: %s <%s>" +msgstr "Tổ chức: %s <%s>" + +#: ../calendar/gui/print.c:2328 +#, c-format +msgid "Location: %s" +msgstr "Địa điểm: %s" + +#. To Translators: It will display "Time: ActualStartDateAndTime (DurationOfTheMeeting)" +#: ../calendar/gui/e-calendar-view.c:1996 +#, c-format +msgid "Time: %s %s" +msgstr "Giờ : %s %s" + +#: ../libedataserver/e-time-utils.c:413 +msgid "%a %m/%d/%Y %H:%M:%S" +msgstr "%a %d/%m/%Y %H:%M:%S" + +#: ../libedataserver/e-time-utils.c:422 +msgid "%a %m/%d/%Y %I:%M:%S %p" +msgstr "%a %d/%m/%Y %I:%M:%S %p" + +#: ../calendar/gui/e-cell-date-edit-text.c:123 +#, c-format +msgid "" +"The date must be entered in the format: \n" +"\n" +"%s" +msgstr "" +"Ngày phải được nhập theo dạng thức: \n" +"\n" +"%s" + +#: ../calendar/gui/e-day-view-time-item.c:553 +#, c-format +msgid "%02i minute divisions" +msgstr "lệch %02i phút" + +#: ../calendar/gui/e-day-view.c:1497 ../calendar/gui/print.c:1517 +msgid "%A %d %B" +msgstr "%A %d %B" + +#. String to use in 12-hour time format for times in the morning. +#: ../calendar/gui/e-day-view.c:748 ../calendar/gui/e-week-view.c:512 +#: ../calendar/gui/print.c:841 ../calendar/gui/e-day-view.c:750 +msgid "am" +msgstr "sáng" + +#. String to use in 12-hour time format for times in the afternoon. +#: ../calendar/gui/e-day-view.c:751 ../calendar/gui/e-week-view.c:515 +#: ../calendar/gui/print.c:843 ../calendar/gui/e-day-view.c:753 +msgid "pm" +msgstr "chiều/tối" + +#: ../calendar/gui/e-itip-control.c:762 ../calendar/gui/e-itip-control.c:761 +msgid "Yes. (Complex Recurrence)" +msgstr "Có. (Lặp lại phức tạp)" + +#: ../calendar/gui/e-itip-control.c:773 ../calendar/gui/e-itip-control.c:772 +#: ../src/crontabEditor.py:83 ../src/crontabEditorHelper.py:91 +#, c-format +msgid "Every day" +msgid_plural "Every %d days" +msgstr[0] "Mỗi %d ngày" + +#: ../calendar/gui/e-itip-control.c:778 ../calendar/gui/e-itip-control.c:777 +#: ../src/crontabEditor.py:85 +#, c-format +msgid "Every week" +msgid_plural "Every %d weeks" +msgstr[0] "Mỗi %d tuần" + +#: ../calendar/gui/e-itip-control.c:780 ../calendar/gui/e-itip-control.c:779 +#, c-format +msgid "Every week on " +msgid_plural "Every %d weeks on " +msgstr[0] "Mỗi %d tuần vào " + +#: ../calendar/gui/e-itip-control.c:788 src/reduce.c:403 +#: dpkg-split/queue.c:166 +#, c-format +msgid " and " +msgstr " và " + +#: ../calendar/gui/e-itip-control.c:795 ../calendar/gui/e-itip-control.c:794 +#, c-format +msgid "The %s day of " +msgstr "Ngày thứ %s của " + +#: ../calendar/gui/e-itip-control.c:808 ../calendar/gui/e-itip-control.c:807 +#, c-format +msgid "The %s %s of " +msgstr "%s %s của " + +#: ../calendar/gui/e-itip-control.c:815 ../calendar/gui/e-itip-control.c:814 +#: ../src/lang.py:281 +#, c-format +msgid "every month" +msgid_plural "every %d months" +msgstr[0] "mỗi %d tháng" + +#: ../calendar/gui/e-itip-control.c:819 ../calendar/gui/e-itip-control.c:818 +#, c-format +msgid "Every year" +msgid_plural "Every %d years" +msgstr[0] "mỗi %d năm" + +#: ../calendar/gui/e-itip-control.c:830 ../calendar/gui/e-itip-control.c:829 +#, c-format +msgid "a total of %d time" +msgid_plural "a total of %d time" +msgstr[0] "tổng cộng giờ %d" + +#: ../calendar/gui/e-itip-control.c:839 ../calendar/gui/e-itip-control.c:838 +msgid ", ending on " +msgstr ", kết thúc vào" + +#: ../calendar/gui/e-itip-control.c:863 ../calendar/gui/e-itip-control.c:862 +#: ../storage/sunone-invitation-list.c:494 +msgid "Starts" +msgstr "Bắt đầu" + +#: ../calendar/gui/e-itip-control.c:876 ../calendar/gui/e-itip-control.c:875 +msgid "Ends" +msgstr "Kết thúc" + +#: ../calendar/gui/e-itip-control.c:910 +#: ../plugins/save-calendar/csv-format.c:395 +#: ../calendar/gui/e-itip-control.c:909 datebook_gui.c:4331 todo_gui.c:2132 +#: ../mimedir/mimedir-vcomponent.c:328 +#, fuzzy +msgid "Due" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Đến hạn\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"Đến hạn\n" +"#-#-#-#-# libmimedir.vi.po (libmimedir HEADnReport-Msgid-Bugs-To: ) #-#-#-" +"#-#\n" +"Tới hạn" + +#: ../calendar/gui/e-itip-control.c:950 ../calendar/gui/e-itip-control.c:1007 +#: ../calendar/gui/e-itip-control.c:949 ../calendar/gui/e-itip-control.c:1006 +msgid "iCalendar Information" +msgstr "Thông tin iCalendar" + +#. Title +#: ../calendar/gui/e-itip-control.c:967 ../calendar/gui/e-itip-control.c:966 +msgid "iCalendar Error" +msgstr "Lỗi iCalendar" + +#: ../plugins/itip-formatter/itip-view.c:404 ../storage/sunone-itip-view.c:293 +#: ../storage/sunone-itip-view.c:294 ../storage/sunone-itip-view.c:364 +#: ../storage/sunone-itip-view.c:365 +msgid "An unknown person" +msgstr "Người lạ" + +#. Describe what the user can do +#: ../calendar/gui/e-itip-control.c:1090 ../calendar/gui/e-itip-control.c:1089 +msgid "" +"
Please review the following information, and then select an action from " +"the menu below." +msgstr "" +"
Vui lòng xem lại các chỉ dẫn sau và chọn một hành động từ trình đơn bên " +"dưới." + +#: ../plugins/itip-formatter/itip-formatter.c:1628 +#: ../storage/sunone-invitation-list.c:522 +#: ../storage/sunone-invitation-list-model.c:355 +#: ../storage/sunone-invitation-list-model.c:694 +msgid "Accepted" +msgstr "Đã chấp nhận" + +#: ../plugins/itip-formatter/itip-formatter.c:1631 +msgid "Tentatively Accepted" +msgstr "Tạm đã chấp nhận" + +#: ../plugins/itip-formatter/itip-formatter.c:1634 +#: ../storage/sunone-invitation-list.c:523 +#: ../storage/sunone-invitation-list-model.c:358 +#: ../storage/sunone-invitation-list-model.c:696 +msgid "Declined" +msgstr "Bị từ chối" + +#: ../calendar/gui/e-itip-control.c:1230 ../calendar/gui/e-itip-control.c:1229 +msgid "" +"The meeting has been cancelled, however it could not be found in your " +"calendars" +msgstr "" +"Cuộc họp đã bị hủy, tuy nhiên không tìm thấy cuộc họp trong lịch của bạn." + +#: ../calendar/gui/e-itip-control.c:1232 ../calendar/gui/e-itip-control.c:1231 +msgid "" +"The task has been cancelled, however it could not be found in your task lists" +msgstr "" +"Tác vụ đã bị hủy, tuy nhiên không tìm thấy tác vụ trong danh sách tác vụ của " +"bạn." + +#: ../calendar/gui/e-itip-control.c:1311 ../calendar/gui/e-itip-control.c:1310 +#, c-format +msgid "%s has published meeting information." +msgstr "%s đã xuất bản tin tức cuộc họp." + +#: ../calendar/gui/e-itip-control.c:1312 ../calendar/gui/e-itip-control.c:1311 +msgid "Meeting Information" +msgstr "Tin tức cuộc họp" + +#: ../calendar/gui/e-itip-control.c:1318 ../calendar/gui/e-itip-control.c:1317 +#, c-format +msgid "%s requests the presence of %s at a meeting." +msgstr "%s yêu cầu sự hiện diện của %s tại cuộc họp." + +#: ../calendar/gui/e-itip-control.c:1320 ../calendar/gui/e-itip-control.c:1319 +#, c-format +msgid "%s requests your presence at a meeting." +msgstr "%s yêu cầu sự hiện diện của bạn tại cuộc họp." + +#: ../calendar/gui/e-itip-control.c:1321 ../calendar/gui/e-itip-control.c:1320 +msgid "Meeting Proposal" +msgstr "Đề nghị cuộc họp" + +#. FIXME Whats going on here? +#: ../calendar/gui/e-itip-control.c:1327 ../calendar/gui/e-itip-control.c:1326 +#, c-format +msgid "%s wishes to add to an existing meeting." +msgstr "%s muốn thêm vào một cuộc họp đã có." + +#: ../calendar/gui/e-itip-control.c:1328 ../calendar/gui/e-itip-control.c:1327 +msgid "Meeting Update" +msgstr "Cập nhật cuộc họp" + +#: ../calendar/gui/e-itip-control.c:1332 ../calendar/gui/e-itip-control.c:1331 +#, c-format +msgid "%s wishes to receive the latest meeting information." +msgstr "%s muốn nhận tin tức về cuộc họp." + +#: ../calendar/gui/e-itip-control.c:1333 ../calendar/gui/e-itip-control.c:1332 +msgid "Meeting Update Request" +msgstr "Yêu cầu cập nhật cuộc họp" + +#: ../calendar/gui/e-itip-control.c:1340 ../calendar/gui/e-itip-control.c:1339 +#, c-format +msgid "%s has replied to a meeting request." +msgstr "%s đã trả lời yêu cầu họp." + +#: ../calendar/gui/e-itip-control.c:1341 ../calendar/gui/e-itip-control.c:1340 +msgid "Meeting Reply" +msgstr "Trả lời họp" + +#: ../calendar/gui/e-itip-control.c:1348 ../calendar/gui/e-itip-control.c:1347 +#, c-format +msgid "%s has cancelled a meeting." +msgstr "%s đã hủy bỏ cuộc họp." + +#: ../calendar/gui/e-itip-control.c:1349 ../calendar/gui/e-itip-control.c:1348 +msgid "Meeting Cancellation" +msgstr "Hủy bỏ cuộc họp" + +#: ../calendar/gui/e-itip-control.c:1359 ../calendar/gui/e-itip-control.c:1436 +#: ../calendar/gui/e-itip-control.c:1476 ../calendar/gui/e-itip-control.c:1358 +#: ../calendar/gui/e-itip-control.c:1435 ../calendar/gui/e-itip-control.c:1475 +#, c-format +msgid "%s has sent an unintelligible message." +msgstr "%s đã gởi một thông điệp không thể hiểu." + +#: ../calendar/gui/e-itip-control.c:1360 ../calendar/gui/e-itip-control.c:1359 +msgid "Bad Meeting Message" +msgstr "Thông điệp sai về cuộc họp" + +#: ../calendar/gui/e-itip-control.c:1387 ../calendar/gui/e-itip-control.c:1386 +#, c-format +msgid "%s has published task information." +msgstr "%s đã xuất bản tin tức tác vụ." + +#: ../calendar/gui/e-itip-control.c:1388 ../calendar/gui/e-itip-control.c:1387 +msgid "Task Information" +msgstr "Tin tức tác vụ" + +#: ../calendar/gui/e-itip-control.c:1395 ../calendar/gui/e-itip-control.c:1394 +#, c-format +msgid "%s requests %s to perform a task." +msgstr "%s yêu cầu %s để thực hiện tác vụ." + +#: ../calendar/gui/e-itip-control.c:1397 ../calendar/gui/e-itip-control.c:1396 +#, c-format +msgid "%s requests you perform a task." +msgstr "%s yêu cầu bạn thực hiện tác vụ." + +#: ../calendar/gui/e-itip-control.c:1398 ../calendar/gui/e-itip-control.c:1397 +msgid "Task Proposal" +msgstr "Đề nghị tác vụ" + +#. FIXME Whats going on here? +#: ../calendar/gui/e-itip-control.c:1404 ../calendar/gui/e-itip-control.c:1403 +#, c-format +msgid "%s wishes to add to an existing task." +msgstr "%s muốn thêm vào tác vụ đã có." + +#: ../calendar/gui/e-itip-control.c:1405 ../calendar/gui/e-itip-control.c:1404 +msgid "Task Update" +msgstr "Cập nhật tác vụ" + +#: ../calendar/gui/e-itip-control.c:1409 ../calendar/gui/e-itip-control.c:1408 +#, c-format +msgid "%s wishes to receive the latest task information." +msgstr "%s muốn nhận tin tức về tác vụ." + +#: ../calendar/gui/e-itip-control.c:1410 ../calendar/gui/e-itip-control.c:1409 +msgid "Task Update Request" +msgstr "Yêu cầu cập nhật tác vụ" + +#: ../calendar/gui/e-itip-control.c:1417 ../calendar/gui/e-itip-control.c:1416 +#, c-format +msgid "%s has replied to a task assignment." +msgstr "%s đã trả lời về cách gán tác vụ." + +#: ../calendar/gui/e-itip-control.c:1418 ../calendar/gui/e-itip-control.c:1417 +msgid "Task Reply" +msgstr "Trả lời tác vụ" + +#: ../calendar/gui/e-itip-control.c:1425 ../calendar/gui/e-itip-control.c:1424 +#, c-format +msgid "%s has cancelled a task." +msgstr "%s đã hủy bỏ tác vụ." + +#: ../calendar/gui/e-itip-control.c:1426 ../calendar/gui/e-itip-control.c:1425 +msgid "Task Cancellation" +msgstr "Tác vụ bị hủy bỏ" + +#: ../calendar/gui/e-itip-control.c:1437 ../calendar/gui/e-itip-control.c:1436 +msgid "Bad Task Message" +msgstr "Thông điệp tác vụ sai" + +#: ../calendar/gui/e-itip-control.c:1461 ../calendar/gui/e-itip-control.c:1460 +#, c-format +msgid "%s has published free/busy information." +msgstr "%s đã xuất bản tin tức Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:1462 ../calendar/gui/e-itip-control.c:1461 +msgid "Free/Busy Information" +msgstr "Tin tức Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:1466 ../calendar/gui/e-itip-control.c:1465 +#, c-format +msgid "%s requests your free/busy information." +msgstr "%s yêu cầu tin tức Rảnh/Bận của bạn." + +#: ../calendar/gui/e-itip-control.c:1467 ../calendar/gui/e-itip-control.c:1466 +msgid "Free/Busy Request" +msgstr "Yêu cầu tin tức Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:1471 ../calendar/gui/e-itip-control.c:1470 +#, c-format +msgid "%s has replied to a free/busy request." +msgstr "%s đã trả lời yêu cầu tin tức Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:1472 ../calendar/gui/e-itip-control.c:1471 +msgid "Free/Busy Reply" +msgstr "Trả lời tin tức Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:1477 ../calendar/gui/e-itip-control.c:1476 +msgid "Bad Free/Busy Message" +msgstr "Thông điệp Rảnh/Bận sai" + +#: ../calendar/gui/e-itip-control.c:1552 ../calendar/gui/e-itip-control.c:1551 +msgid "The message does not appear to be properly formed" +msgstr "Thông điệp có lẽ không đúng hình thức." + +#: ../calendar/gui/e-itip-control.c:1611 ../calendar/gui/e-itip-control.c:1610 +msgid "The message contains only unsupported requests." +msgstr "Thông điệp chỉ chứa yêu cầu chưa được hỗ trợ." + +#: ../calendar/gui/e-itip-control.c:1644 ../calendar/gui/e-itip-control.c:1643 +msgid "The attachment does not contain a valid calendar message" +msgstr "Đính kèm không chứa thông điệp lịch hợp lệ." + +#: ../calendar/gui/e-itip-control.c:1676 ../calendar/gui/e-itip-control.c:1675 +msgid "The attachment has no viewable calendar items" +msgstr "Đính kèm không chứa mục lịch nào có thể xem được." + +#: ../calendar/gui/e-itip-control.c:1910 ../calendar/gui/e-itip-control.c:1909 +msgid "Update complete\n" +msgstr "Mới cập nhật xong\n" + +#: ../calendar/gui/e-itip-control.c:1938 ../calendar/gui/e-itip-control.c:1937 +msgid "Object is invalid and cannot be updated\n" +msgstr "Đối tượng không hợp lệ nên không thể cập nhật nó.\n" + +#: ../calendar/gui/e-itip-control.c:1948 ../calendar/gui/e-itip-control.c:1947 +msgid "This response is not from a current attendee. Add as an attendee?" +msgstr "" +"Hồi đáp này không phải đến từ một người dự hiện thời. Thêm người này như là " +"người dự nhé?" + +#: ../calendar/gui/e-itip-control.c:1960 ../calendar/gui/e-itip-control.c:1959 +msgid "Attendee status could not be updated because of an invalid status!\n" +msgstr "Không thể cập nhật trạng thái người dự vì trạng thái không hợp lệ!\n" + +#: ../calendar/gui/e-itip-control.c:1977 ../calendar/gui/e-itip-control.c:1976 +msgid "Attendee status updated\n" +msgstr "Mới cập nhật trạng thái người dự\n" + +#: ../plugins/itip-formatter/itip-formatter.c:1013 +msgid "Attendee status can not be updated because the item no longer exists" +msgstr "Không thể cập nhật trạng thái người dự vì không còn có lại mục đó." + +#: ../calendar/gui/e-itip-control.c:2010 ../calendar/gui/e-itip-control.c:2009 +msgid "Removal Complete" +msgstr "Mới gỡ bỏ xong" + +#: ../calendar/gui/e-itip-control.c:2033 ../calendar/gui/e-itip-control.c:2081 +#: ../calendar/gui/e-itip-control.c:2032 ../calendar/gui/e-itip-control.c:2080 +msgid "Item sent!\n" +msgstr "Mục đã được gởi.\n" + +#: ../calendar/gui/e-itip-control.c:2035 ../calendar/gui/e-itip-control.c:2085 +#: ../calendar/gui/e-itip-control.c:2034 ../calendar/gui/e-itip-control.c:2084 +msgid "The item could not be sent!\n" +msgstr "Không thể gởi mục này.\n" + +#: ../calendar/gui/e-itip-control.c:2165 ../calendar/gui/e-itip-control.c:2164 +msgid "Choose an action:" +msgstr "Chọn hành động:" + +#: ../calendar/gui/e-itip-control.c:2264 +#: ../plugins/groupwise-features/process-meeting.c:48 src/fe-gtk/dccgui.c:580 +#: src/fe-gtk/dccgui.c:880 ../calendar/gui/e-itip-control.c:2263 +#: src/fe-gtk/dccgui.c:582 src/fe-gtk/dccgui.c:882 +msgid "Accept" +msgstr "Chấp nhận" + +#: ../calendar/gui/e-itip-control.c:2265 ../calendar/gui/e-itip-control.c:2264 +msgid "Tentatively accept" +msgstr "Tạm chấp nhận" + +#: ../calendar/gui/e-itip-control.c:2266 +#: ../plugins/groupwise-features/process-meeting.c:50 +#: ../calendar/gui/e-itip-control.c:2265 +msgid "Decline" +msgstr "Từ chối" + +#: ../calendar/gui/e-itip-control.c:2295 ../calendar/gui/e-itip-control.c:2294 +msgid "Send Free/Busy Information" +msgstr "Gởi tin tức Rảnh/Bận" + +#: ../calendar/gui/e-itip-control.c:2323 ../calendar/gui/e-itip-control.c:2322 +msgid "Update respondent status" +msgstr "Cập nhật trạng thái trả lời" + +#: ../calendar/gui/e-itip-control.c:2351 ../calendar/gui/e-itip-control.c:2350 +msgid "Send Latest Information" +msgstr "Gởi tin tức" + +# Variable: do not translate/ biến: đừng dịch +#: ../calendar/gui/e-itip-control.glade.h:2 +#, no-c-format +msgid "%P %%" +msgstr "%P %%" + +#: ../calendar/gui/e-itip-control.glade.h:3 +msgid "--to--" +msgstr "--tới--" + +#: ../calendar/gui/e-itip-control.glade.h:4 +msgid "Calendar Message" +msgstr "Thông điệp lịch" + +#: ../calendar/gui/e-itip-control.glade.h:7 +msgid "Loading Calendar" +msgstr "Đang tải lịch" + +#: ../calendar/gui/e-itip-control.glade.h:8 +msgid "Loading calendar..." +msgstr "Đang tải lịch..." + +#: ../calendar/gui/e-itip-control.glade.h:10 +msgid "Server Message:" +msgstr "Thông điệp máy phục vụ :" + +#: ../calendar/gui/e-itip-control.glade.h:12 +msgid "date-end" +msgstr "ngày-cuối" + +#: ../calendar/gui/e-itip-control.glade.h:13 +msgid "date-start" +msgstr "ngày-đầu" + +#: ../calendar/gui/e-meeting-list-view.c:69 +msgid "Chair Persons" +msgstr "Người chủ trì" + +#: ../calendar/gui/e-meeting-list-view.c:70 +#: ../calendar/gui/e-meeting-list-view.c:153 +msgid "Required Participants" +msgstr "Người dự yêu cầu" + +#: ../calendar/gui/e-meeting-list-view.c:71 +msgid "Optional Participants" +msgstr "Người dự tùy chọn" + +#: ../src/interface.c:909 ../src/procdialogs.c:652 +msgid "Resources" +msgstr "Tài nguyên" + +#: ../calendar/gui/e-meeting-store.c:116 ../calendar/gui/e-meeting-store.c:802 +msgid "Individual" +msgstr "Riêng lẻ" + +#: ../glom/data_structure/layout/layoutgroup.cc:292 ../pan/score-add-ui.c:599 +#: ../widgets/gtk+.xml.in.h:82 ../mimedir/mimedir-attribute.c:137 +msgid "Group" +msgstr "Nhóm" + +#: ../calendar/gui/e-meeting-store.c:120 ../objects/Istar/other.c:73 +msgid "Resource" +msgstr "Tài nguyên" + +#: ../calendar/gui/e-meeting-store.c:105 ../calendar/gui/e-meeting-store.c:122 +msgid "Room" +msgstr "Phòng" + +#: ../calendar/gui/e-meeting-store.c:134 ../calendar/gui/e-meeting-store.c:151 +msgid "Chair" +msgstr "Chủ trì" + +#: ../calendar/gui/e-meeting-store.c:153 ../calendar/gui/e-meeting-store.c:805 +msgid "Required Participant" +msgstr "Người dự cần thiết" + +#: ../calendar/gui/e-meeting-store.c:138 ../calendar/gui/e-meeting-store.c:155 +msgid "Optional Participant" +msgstr "Người dự tùy chọn" + +#: ../calendar/gui/e-meeting-store.c:140 ../calendar/gui/e-meeting-store.c:157 +msgid "Non-Participant" +msgstr "Người không tham dự" + +#: ../calendar/gui/e-meeting-store.c:209 ../calendar/gui/e-meeting-store.c:815 +#: ../storage/sunone-invitation-list.c:521 +#: ../storage/sunone-invitation-list-model.c:352 +#: ../storage/sunone-invitation-list-model.c:692 +msgid "Needs Action" +msgstr "Cần hành động" + +#: ../calendar/gui/e-meeting-time-sel.c:411 +#: ../storage/sunone-invitation-list.c:524 +#: ../storage/sunone-invitation-list-model.c:361 +#: ../storage/sunone-invitation-list-model.c:698 +msgid "Tentative" +msgstr "Chưa chắc" + +#: ../plugins/itip-formatter/itip-formatter.c:1637 +msgid "Delegated" +msgstr "Ủy nhiệm" + +#. The extra space is just a hack to occupy more space for Attendee +#: ../calendar/gui/e-meeting-list-view.c:463 +msgid "Attendee " +msgstr "Người dự " + +#: ../calendar/gui/e-meeting-store.c:198 ../calendar/gui/e-meeting-store.c:221 +msgid "In Process" +msgstr "Trong tiến trình" + +#. This is a strftime() format string %A = full weekday name, +#. %B = full month name, %d = month day, %Y = full year. +#: ../calendar/gui/e-meeting-time-sel.c:2104 +msgid "%A, %B %d, %Y" +msgstr "%A, %d %B, %Y" + +#: ../libedataserver/e-time-utils.c:404 +msgid "%a %m/%d/%Y" +msgstr "%a %d/%m/%Y" + +#: ../libedataserver/e-time-utils.c:242 ../libedataserver/e-time-utils.c:303 +msgid "%m/%d/%Y" +msgstr "%d/%m/%Y" + +#: ../calendar/gui/e-meeting-time-sel.c:416 ../designs/OOA/ooa.glade.h:11 +#: ../calendar/gui/e-meeting-time-sel.c:413 +msgid "Out of Office" +msgstr "Ở ngoại văn phòng" + +#: ../calendar/gui/e-meeting-time-sel.c:417 +#: ../calendar/gui/e-meeting-time-sel.c:414 +msgid "No Information" +msgstr "Không có thông tin" + +#: ../calendar/gui/e-meeting-time-sel.c:428 +msgid "Con_tacts..." +msgstr "_Liên lạc..." + +#: ../plug-ins/MapObject/mapobject_ui.c:1299 +msgid "O_ptions" +msgstr "Tù_y chọn" + +#: ../calendar/gui/e-meeting-time-sel.c:465 +#: ../calendar/gui/e-meeting-time-sel.c:462 +msgid "Show _only working hours" +msgstr "Chỉ hiện giờ làm _việc" + +#: ../calendar/gui/e-meeting-time-sel.c:475 +#: ../calendar/gui/e-meeting-time-sel.c:472 +msgid "Show _zoomed out" +msgstr "Hiện Thu _nhỏ" + +#: ../calendar/gui/e-meeting-time-sel.c:490 +#: ../calendar/gui/e-meeting-time-sel.c:487 +msgid "_Update free/busy" +msgstr "_Cập nhật Rảnh/Bận" + +#: ../calendar/gui/e-meeting-time-sel.c:505 +#: ../calendar/gui/e-meeting-time-sel.c:502 +msgid "_<<" +msgstr "_<<" + +#: ../calendar/gui/e-meeting-time-sel.c:523 +#: ../calendar/gui/e-meeting-time-sel.c:520 +msgid "_Autopick" +msgstr "_Tự động chọn" + +#: ../calendar/gui/e-meeting-time-sel.c:538 +#: ../calendar/gui/e-meeting-time-sel.c:535 +msgid ">_>" +msgstr ">_>" + +#: ../calendar/gui/e-meeting-time-sel.c:555 +#: ../calendar/gui/e-meeting-time-sel.c:552 +msgid "_All people and resources" +msgstr "_Mọi người và tài nguyên đều" + +#: ../calendar/gui/e-meeting-time-sel.c:564 +#: ../calendar/gui/e-meeting-time-sel.c:561 +msgid "All _people and one resource" +msgstr "Mọi người và mộ_t tài nguyên" + +#: ../calendar/gui/e-meeting-time-sel.c:573 +#: ../calendar/gui/e-meeting-time-sel.c:570 +msgid "_Required people" +msgstr "Người _cần thiết" + +#: ../calendar/gui/e-meeting-time-sel.c:582 +#: ../calendar/gui/e-meeting-time-sel.c:579 +msgid "Required people and _one resource" +msgstr "Người cần thiết _và một tài nguyên" + +#: ../calendar/gui/e-meeting-time-sel.c:615 +msgid "_Start time:" +msgstr "Thời điểm đầ_u:" + +#: ../calendar/gui/e-meeting-time-sel.c:642 +msgid "_End time:" +msgstr "Thời điểm _cuối:" + +#: ../calendar/gui/e-memo-table.c:276 +msgid "Memo Table" +msgstr "Bảng ghi nhớ" + +#: ../calendar/gui/e-memo-table.c:872 +msgid "_Delete Selected Memos" +msgstr "Xoá bỏ các ghi nhớ đã chọn" + +#: ../calendar/gui/e-memo-table.c:995 ../calendar/gui/e-memo-table.etspec.h:2 +msgid "Click to add a memo" +msgstr "Nhấn để thêm ghi nhớ" + +#: ../calendar/gui/e-memo-table.etspec.h:3 +msgid "Memo sort" +msgstr "Sắp xếp ghi nhớ" + +#: ../calendar/gui/gnome-cal.c:2532 +#, c-format +msgid "" +"Error on %s:\n" +" %s" +msgstr "" +"Lỗi khi « %s »:\n" +" %s" + +#: ../calendar/gui/e-memos.c:776 +msgid "Loading memos" +msgstr "Đang tải ghi nhớ" + +#: ../calendar/gui/e-memos.c:861 +#, c-format +msgid "Opening memos at %s" +msgstr "Đang mở ghi nhớ tại %s" + +#: ../calendar/gui/e-memos.c:1034 ../calendar/gui/e-tasks.c:1226 +#: ../calendar/gui/e-tasks.c:1148 +msgid "Deleting selected objects..." +msgstr "Đang xoá bỏ các đối tượng được chọn..." + +#: ../calendar/gui/e-pub-utils.c:322 ../calendar/gui/e-pub-utils.c:300 +#, c-format +msgid "Enter the password for %s" +msgstr "Hãy nhập mật khẩu cho « %s »" + +#: ../calendar/gui/e-tasks.c:871 ../calendar/gui/e-tasks.c:845 +msgid "Loading tasks" +msgstr "Đang tải tác vụ..." + +#: ../calendar/gui/e-tasks.c:958 ../calendar/gui/e-tasks.c:929 +#, c-format +msgid "Opening tasks at %s" +msgstr "Đang mở tác vụ tại %s..." + +#: ../calendar/gui/e-tasks.c:1203 ../calendar/gui/e-tasks.c:1125 +msgid "Completing tasks..." +msgstr "Đang hoàn tất tác vụ..." + +#: ../calendar/gui/e-tasks.c:1253 ../calendar/gui/e-tasks.c:1175 +msgid "Expunging" +msgstr "Đang xoá hẳn" + +#: ../calendar/gui/e-timezone-entry.c:133 +msgid "Timezone Button" +msgstr "Nút múi giờ" + +#. strftime format %d = day of month, %B = full +#. month name. You can change the order but don't +#. change the specifiers or add anything. +#: ../calendar/gui/print.c:1501 +msgid "%d %B" +msgstr "%d %B" + +#: ../calendar/gui/gnome-cal.c:792 ../calendar/gui/gnome-cal.c:789 +msgid "Updating query" +msgstr "Đang cập nhật truy vấn" + +#: ../calendar/gui/gnome-cal.c:2240 +msgid "_Custom View" +msgstr "Khung xem tự _chọn" + +#: ../calendar/gui/gnome-cal.c:2241 +msgid "_Save Custom View" +msgstr "_Lưu khung xem tự chọn" + +#: ../calendar/gui/gnome-cal.c:2246 +msgid "_Define Views..." +msgstr "_Định nghĩa khung xem..." + +#: ../calendar/gui/gnome-cal.c:2408 ../calendar/gui/gnome-cal.c:2378 +#, c-format +msgid "Loading appointments at %s" +msgstr "Đang tải cuộc hẹn lúc « %s »" + +#: ../calendar/gui/gnome-cal.c:2427 ../calendar/gui/gnome-cal.c:2397 +#, c-format +msgid "Loading tasks at %s" +msgstr "Đang tải tác vụ lúc %s..." + +#: ../calendar/gui/gnome-cal.c:3449 ../calendar/gui/gnome-cal.c:3388 +msgid "Purging" +msgstr "Đang tẩy..." + +#: ../calendar/gui/goto-dialog.glade.h:1 ../calendar.inc.php:5 +#: ../logview/log_repaint.c:36 ../gncal/gnomecal-goto.c:285 +#: makeinfo/cmds.c:585 +msgid "April" +msgstr "Tháng Tư" + +#: ../calendar/gui/goto-dialog.glade.h:2 ../calendar.inc.php:7 +#: ../logview/log_repaint.c:37 ../gncal/gnomecal-goto.c:289 +#: makeinfo/cmds.c:586 +msgid "August" +msgstr "Tháng Tám" + +#: ../calendar/gui/goto-dialog.glade.h:3 ../calendar.inc.php:9 +#: ../gncal/gnomecal-goto.c:293 makeinfo/cmds.c:587 +msgid "December" +msgstr "Tháng Chạp" + +#: ../calendar/gui/goto-dialog.glade.h:4 ../logview/log_repaint.c:36 +#: ../gncal/gnomecal-goto.c:283 makeinfo/cmds.c:585 +msgid "February" +msgstr "Tháng Hai" + +#: makeinfo/cmds.c:585 +msgid "January" +msgstr "Tháng Giêng" + +#: makeinfo/cmds.c:586 +msgid "July" +msgstr "Tháng Bảy" + +#: makeinfo/cmds.c:586 +msgid "June" +msgstr "Tháng Sáu" + +#: ../calendar/gui/goto-dialog.glade.h:8 ../calendar.inc.php:5 +#: ../logview/log_repaint.c:36 ../gncal/gnomecal-goto.c:284 +#: makeinfo/cmds.c:585 +msgid "March" +msgstr "Tháng Ba" + +#: ../calendar/gui/goto-dialog.glade.h:9 ../calendar.inc.php:6 +#: ../logview/log_repaint.c:36 ../gncal/gnomecal-goto.c:286 +#: makeinfo/cmds.c:585 +msgid "May" +msgstr "Tháng Năm" + +#: ../calendar/gui/goto-dialog.glade.h:10 ../calendar.inc.php:8 +#: ../logview/log_repaint.c:38 ../gncal/gnomecal-goto.c:292 +#: makeinfo/cmds.c:587 +msgid "November" +msgstr "Tháng Mười Một" + +#: ../calendar/gui/goto-dialog.glade.h:11 ../calendar.inc.php:8 +#: ../logview/log_repaint.c:37 ../gncal/gnomecal-goto.c:291 +#: makeinfo/cmds.c:586 +msgid "October" +msgstr "Tháng Mười" + +#: ../calendar/gui/goto-dialog.glade.h:12 ../libegg/egg-datetime.c:467 +#: ../src/libegg/egg-datetime.c:467 +msgid "Select Date" +msgstr "Chọn ngày" + +#: ../calendar/gui/goto-dialog.glade.h:13 ../calendar.inc.php:7 +#: ../logview/log_repaint.c:37 ../gncal/gnomecal-goto.c:290 +#: makeinfo/cmds.c:586 +msgid "September" +msgstr "Tháng Chín" + +#: ../calendar/gui/goto-dialog.glade.h:14 +msgid "_Select Today" +msgstr "Chọn _hôm nay" + +#: ../calendar/gui/itip-utils.c:402 ../calendar/gui/itip-utils.c:442 +msgid "An organizer must be set." +msgstr "Phải chọn một bộ tổ chức." + +#: ../calendar/gui/itip-utils.c:387 ../calendar/gui/itip-utils.c:389 +msgid "At least one attendee is necessary" +msgstr "Cần ít nhất một người dự." + +#: ../calendar/gui/itip-utils.c:508 ../calendar/gui/itip-utils.c:630 +#: ../calendar/gui/itip-utils.c:510 ../calendar/gui/itip-utils.c:632 +msgid "Event information" +msgstr "Tin tức sự kiện" + +#: ../calendar/gui/itip-utils.c:510 ../calendar/gui/itip-utils.c:632 +#: ../calendar/gui/itip-utils.c:512 ../calendar/gui/itip-utils.c:634 +msgid "Task information" +msgstr "Tin tức tác vụ" + +#: ../calendar/gui/itip-utils.c:512 ../calendar/gui/itip-utils.c:634 +#: ../calendar/gui/itip-utils.c:514 ../calendar/gui/itip-utils.c:636 +msgid "Journal information" +msgstr "Tin tức nhật ký" + +#: ../calendar/gui/itip-utils.c:514 ../calendar/gui/itip-utils.c:652 +#: ../calendar/gui/itip-utils.c:516 ../calendar/gui/itip-utils.c:654 +msgid "Free/Busy information" +msgstr "Tin tức rảnh/bận" + +#: ../calendar/gui/itip-utils.c:516 ../calendar/gui/itip-utils.c:518 +msgid "Calendar information" +msgstr "Tin tức lịch" + +#: ../calendar/gui/itip-utils.c:565 ../calendar/gui/itip-utils.c:567 +#: dselect/pkgdisplay.cc:99 +msgid "Updated" +msgstr "Đã cập nhật" + +#: ../calendar/gui/itip-utils.c:573 ../glade/straw.glade.h:45 +msgid "Refresh" +msgstr "Cập nhật" + +#: ../calendar/gui/itip-utils.c:577 ../calendar/gui/itip-utils.c:579 +msgid "Counter-proposal" +msgstr "Phản đề nghị" + +#: ../calendar/gui/itip-utils.c:648 ../calendar/gui/itip-utils.c:650 +#, c-format +msgid "Free/Busy information (%s to %s)" +msgstr "Tin tức Rảnh/Bận (%s đến %s)" + +#: ../calendar/gui/itip-utils.c:658 ../calendar/gui/itip-utils.c:660 +msgid "iCalendar information" +msgstr "Tin tức iCalendar" + +#: ../calendar/gui/itip-utils.c:813 ../calendar/gui/itip-utils.c:815 +msgid "You must be an attendee of the event." +msgstr "Bạn phải là người dự sự kiện đó." + +#: ../plug-ins/imagemap/imap_cmd_copy_object.c:55 ../glade/gbwidget.c:1866 +#: ../glade/property.c:892 ../glade/property.c:5141 src/floatwin.cpp:114 +#: src/mainwin.cpp:1084 address_gui.c:2717 datebook_gui.c:4388 memo_gui.c:1569 +#: todo_gui.c:2186 Expense/expense.c:1651 KeyRing/keyring.c:1617 +#: po/silky.glade.h:85 app/sample-editor.c:461 +msgid "Copy" +msgstr "Chép" + +#: ../calendar/gui/tasks-component.c:442 ../app/disp_callbacks.c:118 +msgid "Properties..." +msgstr "Thuộc tính..." + +#: ../calendar/gui/memos-component.c:524 +#, c-format +msgid "%d memo" +msgid_plural "%d memo" +msgstr[0] "%d ghi nhớ" + +#: ../calendar/gui/memos-component.c:526 ../calendar/gui/tasks-component.c:517 +#: ../mail/mail-component.c:549 ../mail/mail-component.c:547 +#, c-format +msgid ", %d selected" +msgid_plural ", %d selected" +msgstr[0] ", %d được chọn" + +#: ../calendar/gui/memos-component.c:573 +msgid "Failed upgrading memos." +msgstr "Lỗi nâng cấp ghi nhớ." + +#: ../calendar/gui/memos-component.c:869 +#, c-format +msgid "Unable to open the memo list '%s' for creating events and meetings" +msgstr "Không thể mở danh sách ghi nhớ « %s » để tạo sự kiện và cuộc họp" + +#: ../calendar/gui/memos-component.c:882 +msgid "There is no calendar available for creating memos" +msgstr "Không có lịch nào sẵn sàng để tạo ghi nhớ" + +#: ../calendar/gui/memos-component.c:973 +msgid "Memo Source Selector" +msgstr "Bộ chọn nguồn ghi nhớ" + +#: ../calendar/gui/memos-component.c:1156 +msgid "New memo" +msgstr "Ghi nhớ mới" + +#: ../calendar/gui/memos-component.c:1157 +msgid "_Memo" +msgstr "Ghi _nhớ" + +#: ../calendar/gui/memos-component.c:1158 +msgid "Create a new memo" +msgstr "Tạo ghi nhớ mới" + +#: ../calendar/gui/memos-component.c:1164 +msgid "New memo list" +msgstr "Danh sách ghi nhớ mới" + +#: ../calendar/gui/memos-component.c:1165 +msgid "Memo l_ist" +msgstr "_Danh sách ghi nhớ" + +#: ../calendar/gui/memos-component.c:1166 +msgid "Create a new memo list" +msgstr "Tạo danh sách ghi nhớ mới" + +#: ../calendar/gui/memos-control.c:340 +msgid "Print Memos" +msgstr "In ghi nhớ" + +#: ../calendar/gui/migration.c:156 ../calendar/gui/migration.c:151 +msgid "" +"The location and hierarchy of the Evolution task folders has changed since " +"Evolution 1.x.\n" +"\n" +"Please be patient while Evolution migrates your folders..." +msgstr "" +"Địa chỉ và cây thư mục tác vụ Evolution đã thay đổi so với Evolution phiên " +"bản 1.x.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi các thư mục..." + +#: ../calendar/gui/migration.c:160 ../calendar/gui/migration.c:155 +msgid "" +"The location and hierarchy of the Evolution calendar folders has changed " +"since Evolution 1.x.\n" +"\n" +"Please be patient while Evolution migrates your folders..." +msgstr "" +"Địa chỉ và cây thư mục lịch Evolution đã thay đổi so với Evolution phiên bản " +"1.x.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi các thư mục..." + +#: ../calendar/gui/migration.c:748 ../calendar/gui/migration.c:915 +msgid "Unable to migrate old settings from evolution/config.xmldb" +msgstr "" +"Không thể chuyển đổi các thiết lập cũ từ tập tin evolution/config.xmldb" + +#: ../calendar/gui/migration.c:782 ../calendar/gui/migration.c:777 +#, c-format +msgid "Unable to migrate calendar `%s'" +msgstr "Không thể chuyển đổi lịch « %s »." + +#: ../calendar/gui/migration.c:948 ../calendar/gui/migration.c:944 +#, c-format +msgid "Unable to migrate tasks `%s'" +msgstr "Không thể chuyển đổi các tác vụ « %s »." + +#: ../calendar/gui/print.c:492 ../calendar/libecal/e-cal-recur.c:4014 +msgid "1st" +msgstr "mồng 1" + +#: ../calendar/gui/print.c:492 ../calendar/libecal/e-cal-recur.c:4015 +msgid "2nd" +msgstr "mồng 2" + +#: ../calendar/gui/print.c:492 ../calendar/libecal/e-cal-recur.c:4016 +msgid "3rd" +msgstr "mồng 3" + +#: ../calendar/gui/print.c:492 ../calendar/libecal/e-cal-recur.c:4017 +#: datebook_gui.c:1558 +#, fuzzy +msgid "4th" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"mồng 4\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"thứ 4" + +#: ../calendar/gui/print.c:492 ../calendar/libecal/e-cal-recur.c:4018 +msgid "5th" +msgstr "mồng 5" + +#: ../calendar/gui/print.c:493 ../calendar/libecal/e-cal-recur.c:4019 +msgid "6th" +msgstr "mồng 6" + +#: ../calendar/gui/print.c:493 ../calendar/libecal/e-cal-recur.c:4020 +msgid "7th" +msgstr "mồng 7" + +#: ../calendar/gui/print.c:493 ../calendar/libecal/e-cal-recur.c:4021 +msgid "8th" +msgstr "mồng 8" + +#: ../calendar/gui/print.c:493 ../calendar/libecal/e-cal-recur.c:4022 +msgid "9th" +msgstr "mồng 9" + +#: ../calendar/gui/print.c:493 ../calendar/libecal/e-cal-recur.c:4023 +msgid "10th" +msgstr "mồng 10" + +#: ../calendar/gui/print.c:494 ../calendar/libecal/e-cal-recur.c:4024 +msgid "11th" +msgstr "ngày 11" + +#: ../calendar/gui/print.c:494 ../calendar/libecal/e-cal-recur.c:4025 +msgid "12th" +msgstr "ngày 12" + +#: ../calendar/gui/print.c:494 ../calendar/libecal/e-cal-recur.c:4026 +msgid "13th" +msgstr "ngày 13" + +#: ../calendar/gui/print.c:494 ../calendar/libecal/e-cal-recur.c:4027 +msgid "14th" +msgstr "ngày 14" + +#: ../calendar/gui/print.c:494 ../calendar/libecal/e-cal-recur.c:4028 +msgid "15th" +msgstr "ngày 15" + +#: ../calendar/gui/print.c:495 ../calendar/libecal/e-cal-recur.c:4029 +msgid "16th" +msgstr "ngày 16" + +#: ../calendar/gui/print.c:495 ../calendar/libecal/e-cal-recur.c:4030 +msgid "17th" +msgstr "ngày 17" + +#: ../calendar/gui/print.c:495 ../calendar/libecal/e-cal-recur.c:4031 +msgid "18th" +msgstr "ngày 18" + +#: ../calendar/gui/print.c:495 ../calendar/libecal/e-cal-recur.c:4032 +msgid "19th" +msgstr "ngày 19" + +#: ../calendar/gui/print.c:495 ../calendar/libecal/e-cal-recur.c:4033 +msgid "20th" +msgstr "ngày 20" + +#: ../calendar/gui/print.c:496 ../calendar/libecal/e-cal-recur.c:4034 +msgid "21st" +msgstr "ngày 21" + +#: ../calendar/gui/print.c:496 ../calendar/libecal/e-cal-recur.c:4035 +msgid "22nd" +msgstr "ngày 22" + +#: ../calendar/gui/print.c:496 ../calendar/libecal/e-cal-recur.c:4036 +msgid "23rd" +msgstr "ngày 23" + +#: ../calendar/gui/print.c:496 ../calendar/libecal/e-cal-recur.c:4037 +msgid "24th" +msgstr "ngày 24" + +#: ../calendar/gui/print.c:496 ../calendar/libecal/e-cal-recur.c:4038 +msgid "25th" +msgstr "ngày 25" + +#: ../calendar/gui/print.c:497 ../calendar/libecal/e-cal-recur.c:4039 +msgid "26th" +msgstr "ngày 26" + +#: ../calendar/gui/print.c:497 ../calendar/libecal/e-cal-recur.c:4040 +msgid "27th" +msgstr "ngày 27" + +#: ../calendar/gui/print.c:497 ../calendar/libecal/e-cal-recur.c:4041 +msgid "28th" +msgstr "ngày 28" + +#: ../calendar/gui/print.c:497 ../calendar/libecal/e-cal-recur.c:4042 +msgid "29th" +msgstr "ngày 29" + +#: ../calendar/gui/print.c:497 ../calendar/libecal/e-cal-recur.c:4043 +msgid "30th" +msgstr "ngày 30" + +#: ../calendar/gui/print.c:498 ../calendar/libecal/e-cal-recur.c:4044 +msgid "31st" +msgstr "ngày 31" + +#: ../calendar/gui/print.c:573 datebook_gui.c:222 datebook_gui.c:229 +#: datebook_gui.c:4078 datebook_gui.c:4085 +msgid "Su" +msgstr "CN" + +#: ../calendar/gui/print.c:573 datebook_gui.c:223 datebook_gui.c:4079 +#, fuzzy +msgid "Mo" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Hai\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"T2" + +#: ../calendar/gui/print.c:573 datebook_gui.c:224 datebook_gui.c:4080 +#, fuzzy +msgid "Tu" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Ba\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"T3" + +#: ../calendar/gui/print.c:573 datebook_gui.c:225 datebook_gui.c:4081 +#, fuzzy +msgid "We" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Tư\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"T4" + +#: ../calendar/gui/print.c:574 datebook_gui.c:226 datebook_gui.c:4082 +#, fuzzy +msgid "Th" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Năm\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"T5" + +#: ../calendar/gui/print.c:574 datebook_gui.c:227 datebook_gui.c:4083 +#, fuzzy +msgid "Fr" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Sáu\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"T6" + +#: ../calendar/gui/print.c:574 datebook_gui.c:228 datebook_gui.c:4084 +#, fuzzy +msgid "Sa" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Bảy\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"T7" + +#. Day +#: ../calendar/gui/print.c:1926 ../calendar/gui/print.c:1917 +msgid "Selected day (%a %b %d %Y)" +msgstr "Ngày được chọn (%a %d %b %Y)" + +#: ../calendar/gui/print.c:1951 ../calendar/gui/print.c:1955 +#: ../calendar/gui/print.c:1942 ../calendar/gui/print.c:1946 +msgid "%a %b %d" +msgstr "%a %d %b" + +#: ../calendar/gui/print.c:1952 ../calendar/gui/print.c:1943 +msgid "%a %d %Y" +msgstr "%a %d %Y" + +#: ../calendar/gui/print.c:1949 ../calendar/gui/print.c:1950 +msgid "%a %b %d %Y" +msgstr "%a %d %b %Y" + +#: ../calendar/gui/print.c:1963 ../calendar/gui/print.c:1954 +#, c-format +msgid "Selected week (%s - %s)" +msgstr "Tuần được chọn (%s - %s)" + +#. Month +#: ../calendar/gui/print.c:1971 ../calendar/gui/print.c:1962 +msgid "Selected month (%b %Y)" +msgstr "Tháng được chọn (%b %Y)" + +#. Year +#: ../calendar/gui/print.c:1978 ../calendar/gui/print.c:1969 +msgid "Selected year (%Y)" +msgstr "Năm được chọn (%Y)" + +#: ../calendar/gui/print.c:2307 event-ui.c:656 +#: ../calendar/gui/dialogs/event-editor.c:141 ../calendar/gui/print.c:2298 +#: alarms.c:533 datebook_gui.c:4279 +msgid "Appointment" +msgstr "Cuộc hẹn" + +#: ../calendar/gui/print.c:2309 event-ui.c:659 ../objects/Istar/other.c:74 +#: ../calendar/gui/dialogs/task-editor.c:138 ../calendar/gui/print.c:2300 +#: datebook_gui.c:4330 todo_gui.c:2131 +msgid "Task" +msgstr "Tác vụ" + +#: ../calendar/gui/print.c:2331 ../calendar/gui/print.c:2320 +#, c-format +msgid "Summary: %s" +msgstr "Tóm tắt: %s" + +#: ../calendar/gui/print.c:2382 ../calendar/gui/print.c:2371 +#, c-format +msgid "Status: %s" +msgstr "Trạng thái: %s" + +#: ../calendar/gui/print.c:2399 ../calendar/gui/print.c:2388 +#, c-format +msgid "Priority: %s" +msgstr "Độ ưu tiên: %s" + +#: ../calendar/gui/print.c:2411 ../calendar/gui/print.c:2400 +#, c-format +msgid "Percent Complete: %i" +msgstr "Phần trăm hoàn thành: %i" + +#: ../calendar/gui/print.c:2437 ../calendar/gui/print.c:2426 +#, c-format +msgid "Categories: %s" +msgstr "Phân loại: %s" + +#: ../calendar/gui/print.c:2448 ../calendar/gui/print.c:2437 +msgid "Contacts: " +msgstr "Liên lạc: " + +#: ../gedit/gedit-ui.xml.h:39 +msgid "Print Preview" +msgstr "Xem thử bản in" + +#: ../calendar/gui/print.c:2623 ../calendar/gui/print.c:2612 +msgid "Print Item" +msgstr "In mục" + +#: ../calendar/gui/tasks-component.c:439 +msgid "_New Task List" +msgstr "Danh sách tác vụ _mới" + +#: ../calendar/gui/tasks-component.c:515 +#, c-format +msgid "%d task" +msgid_plural "%d task" +msgstr[0] "%d tác vụ" + +#: ../calendar/gui/tasks-component.c:564 +msgid "Failed upgrading tasks." +msgstr "Lỗi nâng cấp tác vụ." + +#: ../calendar/gui/tasks-component.c:875 ../calendar/gui/tasks-component.c:872 +#, c-format +msgid "Unable to open the task list '%s' for creating events and meetings" +msgstr "Không thể mở danh sách tác vụ « %s » để tạo sự kiện và cuộc họp." + +#: ../calendar/gui/tasks-component.c:888 ../calendar/gui/tasks-component.c:887 +msgid "There is no calendar available for creating tasks" +msgstr "Không có lịch nào sẵn sàng để tạo tác vụ." + +#: ../calendar/gui/tasks-component.c:988 ../calendar/gui/tasks-component.c:981 +msgid "Task Source Selector" +msgstr "Chưa chọn nguồn." + +#: ../calendar/gui/tasks-component.c:1171 main.c:272 main.c:359 main.c:360 +#: ../calendar/gui/tasks-component.c:1164 +msgid "New task" +msgstr "Tác vụ mới" + +#: ../calendar/gui/tasks-component.c:1173 +#: ../calendar/gui/tasks-component.c:1166 +msgid "Create a new task" +msgstr "Tạo tác vụ mới" + +#: ../calendar/gui/tasks-component.c:1179 +#: ../calendar/gui/tasks-component.c:1172 +msgid "New assigned task" +msgstr "Tác vụ đã gán mới" + +#: ../calendar/gui/tasks-component.c:1180 +#: ../calendar/gui/tasks-component.c:1173 +msgid "Assigne_d Task" +msgstr "Tác vụ đã _gán" + +#: ../calendar/gui/tasks-component.c:1181 +#: ../calendar/gui/tasks-component.c:1174 +msgid "Create a new assigned task" +msgstr "Tạo tác vụ đã gán mới" + +#: ../calendar/gui/tasks-component.c:1187 +#: ../calendar/gui/tasks-component.c:1180 +msgid "New task list" +msgstr "Danh sách tác vụ mới" + +#: ../calendar/gui/tasks-component.c:1188 +#: ../calendar/gui/tasks-component.c:1181 +msgid "Task l_ist" +msgstr "_Danh sách tác vụ" + +#: ../calendar/gui/tasks-component.c:1189 +#: ../calendar/gui/tasks-component.c:1182 +msgid "Create a new task list" +msgstr "Tạo danh sách tác vụ mới" + +#: ../calendar/gui/tasks-control.c:435 ../calendar/gui/tasks-control.c:419 +msgid "" +"This operation will permanently erase all tasks marked as completed. If you " +"continue, you will not be able to recover these tasks.\n" +"\n" +"Really erase these tasks?" +msgstr "" +"Thao tác này sẽ xoá bỏ hoàn toàn mọi tác vụ được đánh dấu đã hoàn tất. Nếu " +"bạn tiếp tục, bạn sẽ không thể phục hồi những tác vụ này.\n" +"\n" +"Bạn có thật sự muốn xoá bỏ những tác vụ này không?" + +#: ../calendar/gui/tasks-control.c:438 ../calendar/gui/tasks-control.c:422 +msgid "Do not ask me again." +msgstr "Đừng hỏi tôi lần nữa." + +#: ../calendar/gui/tasks-control.c:472 ../calendar/gui/tasks-control.c:457 +msgid "Print Tasks" +msgstr "In tác vụ" + +#. The first letter of each day of the week starting with Sunday +#: ../calendar/gui/weekday-picker.c:319 +msgid "SMTWTFS" +msgstr "CHBTNSB" + +#: ../calendar/importers/icalendar-importer.c:79 +#: ../calendar/importers/icalendar-importer.c:78 +msgid "Appointments and Meetings" +msgstr "Cuộc hẹn và Cuộc họp" + +#: ../calendar/importers/icalendar-importer.c:586 +msgid "Opening calendar" +msgstr "Đang mở lịch" + +#: ../calendar/importers/icalendar-importer.c:444 +#: ../calendar/importers/icalendar-importer.c:429 +msgid "iCalendar files (.ics)" +msgstr "Tập tin iCalendar (.ics)" + +#: ../calendar/importers/icalendar-importer.c:445 +#: ../calendar/importers/icalendar-importer.c:430 +msgid "Evolution iCalendar importer" +msgstr "Bộ nhập lịch iCalendar" + +#: ../calendar/importers/icalendar-importer.c:521 +#: ../calendar/importers/icalendar-importer.c:494 +msgid "Reminder!!" +msgstr "••• Bộ nhắc nhở •••" + +#: ../calendar/importers/icalendar-importer.c:573 +#: ../calendar/importers/icalendar-importer.c:539 +msgid "vCalendar files (.vcf)" +msgstr "Tập tin vCalendar (.vcf)" + +#: ../calendar/importers/icalendar-importer.c:574 +#: ../calendar/importers/icalendar-importer.c:540 +msgid "Evolution vCalendar importer" +msgstr "Bộ nhập lịch vCalendar" + +#: ../calendar/importers/icalendar-importer.c:736 +#: ../calendar/importers/icalendar-importer.c:702 +msgid "Calendar Events" +msgstr "Sự kiện lịch" + +#: ../calendar/importers/icalendar-importer.c:773 +#: ../calendar/importers/icalendar-importer.c:739 +msgid "Evolution Calendar intelligent importer" +msgstr "Bộ nhập lịch thông minh Evolution" + +#: ../calendar/zones.h:7 +msgid "Africa/Abidjan" +msgstr "Châu Phi/Abidjan" + +#: ../calendar/zones.h:8 +msgid "Africa/Accra" +msgstr "Châu Phi/Accra" + +#: ../calendar/zones.h:9 +msgid "Africa/Addis_Ababa" +msgstr "Châu Phi/Addis_Ababa" + +#: ../calendar/zones.h:10 +msgid "Africa/Algiers" +msgstr "Châu Phi/Algiers" + +#: ../calendar/zones.h:11 +msgid "Africa/Asmera" +msgstr "Châu Phi/Asmera" + +#: ../calendar/zones.h:12 +msgid "Africa/Bamako" +msgstr "Châu Phi/Bamako" + +#: ../calendar/zones.h:13 +msgid "Africa/Bangui" +msgstr "Châu Phi/Bangui" + +#: ../calendar/zones.h:14 +msgid "Africa/Banjul" +msgstr "Châu Phi/Banjul" + +#: ../calendar/zones.h:15 +msgid "Africa/Bissau" +msgstr "Châu Phi/Bissau" + +#: ../calendar/zones.h:16 +msgid "Africa/Blantyre" +msgstr "Châu Phi/Blantyre" + +#: ../calendar/zones.h:17 +msgid "Africa/Brazzaville" +msgstr "Châu Phi/Brazzaville" + +#: ../calendar/zones.h:18 +msgid "Africa/Bujumbura" +msgstr "Châu Phi/Bujumbura" + +#: ../calendar/zones.h:19 +msgid "Africa/Cairo" +msgstr "Châu Phi/Cairo" + +#: ../calendar/zones.h:20 +msgid "Africa/Casablanca" +msgstr "Châu Phi/Casablanca" + +#: ../calendar/zones.h:21 +msgid "Africa/Ceuta" +msgstr "Châu Phi/Ceuta" + +#: ../calendar/zones.h:22 +msgid "Africa/Conakry" +msgstr "Châu Phi/Conakry" + +#: ../calendar/zones.h:23 +msgid "Africa/Dakar" +msgstr "Châu Phi/Dakar" + +#: ../calendar/zones.h:24 +msgid "Africa/Dar_es_Salaam" +msgstr "Châu Phi/Dar_es_Salaam" + +#: ../calendar/zones.h:25 +msgid "Africa/Djibouti" +msgstr "Châu Phi/Djibouti" + +#: ../calendar/zones.h:26 +msgid "Africa/Douala" +msgstr "Châu Phi/Douala" + +#: ../calendar/zones.h:27 +msgid "Africa/El_Aaiun" +msgstr "Châu Phi/El_Aaiun" + +#: ../calendar/zones.h:28 +msgid "Africa/Freetown" +msgstr "Châu Phi/Freetown" + +#: ../calendar/zones.h:29 +msgid "Africa/Gaborone" +msgstr "Châu Phi/Gaborone" + +#: ../calendar/zones.h:30 +msgid "Africa/Harare" +msgstr "Châu Phi/Harare" + +#: ../calendar/zones.h:31 +msgid "Africa/Johannesburg" +msgstr "Châu Phi/Johannesburg" + +#: ../calendar/zones.h:32 +msgid "Africa/Kampala" +msgstr "Châu Phi/Kampala" + +#: ../calendar/zones.h:33 +msgid "Africa/Khartoum" +msgstr "Châu Phi/Khartoum" + +#: ../calendar/zones.h:34 +msgid "Africa/Kigali" +msgstr "Châu Phi/Kigali" + +#: ../calendar/zones.h:35 +msgid "Africa/Kinshasa" +msgstr "Châu Phi/Kinshasa" + +#: ../calendar/zones.h:36 +msgid "Africa/Lagos" +msgstr "Châu Phi/Lagos" + +#: ../calendar/zones.h:37 +msgid "Africa/Libreville" +msgstr "Châu Phi/Libreville" + +#: ../calendar/zones.h:38 +msgid "Africa/Lome" +msgstr "Châu Phi/Lome" + +#: ../calendar/zones.h:39 +msgid "Africa/Luanda" +msgstr "Châu Phi/Luanda" + +#: ../calendar/zones.h:40 +msgid "Africa/Lubumbashi" +msgstr "Châu Phi/Lubumbashi" + +#: ../calendar/zones.h:41 +msgid "Africa/Lusaka" +msgstr "Châu Phi/Lusaka" + +#: ../calendar/zones.h:42 +msgid "Africa/Malabo" +msgstr "Châu Phi/Malabo" + +#: ../calendar/zones.h:43 +msgid "Africa/Maputo" +msgstr "Châu Phi/Maputo" + +#: ../calendar/zones.h:44 +msgid "Africa/Maseru" +msgstr "Châu Phi/Maseru" + +#: ../calendar/zones.h:45 +msgid "Africa/Mbabane" +msgstr "Châu Phi/Mbabane" + +#: ../calendar/zones.h:46 +msgid "Africa/Mogadishu" +msgstr "Châu Phi/Mogadishu" + +#: ../calendar/zones.h:47 +msgid "Africa/Monrovia" +msgstr "Châu Phi/Monrovia" + +#: ../calendar/zones.h:48 +msgid "Africa/Nairobi" +msgstr "Châu Phi/Nairobi" + +#: ../calendar/zones.h:49 +msgid "Africa/Ndjamena" +msgstr "Châu Phi/Ndjamena" + +#: ../calendar/zones.h:50 +msgid "Africa/Niamey" +msgstr "Châu Phi/Niamey" + +#: ../calendar/zones.h:51 +msgid "Africa/Nouakchott" +msgstr "Châu Phi/Nouakchott" + +#: ../calendar/zones.h:52 +msgid "Africa/Ouagadougou" +msgstr "Châu Phi/Ouagadougou" + +#: ../calendar/zones.h:53 +msgid "Africa/Porto-Novo" +msgstr "Châu Phi/Porto-Novo" + +#: ../calendar/zones.h:54 +msgid "Africa/Sao_Tome" +msgstr "Châu Phi/Sao_Tome" + +#: ../calendar/zones.h:55 +msgid "Africa/Timbuktu" +msgstr "Châu Phi/Timbuktu" + +#: ../calendar/zones.h:56 +msgid "Africa/Tripoli" +msgstr "Châu Phi/Tripoli" + +#: ../calendar/zones.h:57 +msgid "Africa/Tunis" +msgstr "Châu Phi/Tunis" + +#: ../calendar/zones.h:58 +msgid "Africa/Windhoek" +msgstr "Châu Phi/Windhoek" + +#: ../calendar/zones.h:59 +msgid "America/Adak" +msgstr "Châu Mỹ/Adak" + +#: ../calendar/zones.h:60 +msgid "America/Anchorage" +msgstr "Châu Mỹ/Anchorage" + +#: ../calendar/zones.h:61 +msgid "America/Anguilla" +msgstr "Châu Mỹ/Anguilla" + +#: ../calendar/zones.h:62 +msgid "America/Antigua" +msgstr "Châu Mỹ/Antigua" + +#: ../calendar/zones.h:63 +msgid "America/Araguaina" +msgstr "Châu Mỹ/Araguaina" + +#: ../calendar/zones.h:64 +msgid "America/Aruba" +msgstr "Châu Mỹ/Aruba" + +#: ../calendar/zones.h:65 +msgid "America/Asuncion" +msgstr "Châu Mỹ/Asuncion" + +#: ../calendar/zones.h:66 +msgid "America/Barbados" +msgstr "Châu Mỹ/Barbados" + +#: ../calendar/zones.h:67 +msgid "America/Belem" +msgstr "Châu Mỹ/Belem" + +#: ../calendar/zones.h:68 +msgid "America/Belize" +msgstr "Châu Mỹ/Belize" + +#: ../calendar/zones.h:69 +msgid "America/Boa_Vista" +msgstr "Châu Mỹ/Boa_Vista" + +#: ../calendar/zones.h:70 +msgid "America/Bogota" +msgstr "Châu Mỹ/Bogota" + +#: ../calendar/zones.h:71 +msgid "America/Boise" +msgstr "Châu Mỹ/Boise" + +#: ../calendar/zones.h:72 +msgid "America/Buenos_Aires" +msgstr "Châu Mỹ/Buenos_Aires" + +#: ../calendar/zones.h:73 +msgid "America/Cambridge_Bay" +msgstr "Châu Mỹ/Cambridge_Bay" + +#: ../calendar/zones.h:74 +msgid "America/Cancun" +msgstr "Châu Mỹ/Cancun" + +#: ../calendar/zones.h:75 +msgid "America/Caracas" +msgstr "Châu Mỹ/Caracas" + +#: ../calendar/zones.h:76 +msgid "America/Catamarca" +msgstr "Châu Mỹ/Catamarca" + +#: ../calendar/zones.h:77 +msgid "America/Cayenne" +msgstr "Châu Mỹ/Cayenne" + +#: ../calendar/zones.h:78 +msgid "America/Cayman" +msgstr "Châu Mỹ/Cayman" + +#: ../calendar/zones.h:79 +msgid "America/Chicago" +msgstr "Châu Mỹ/Chicago" + +#: ../calendar/zones.h:80 +msgid "America/Chihuahua" +msgstr "Châu Mỹ/Chihuahua" + +#: ../calendar/zones.h:81 +msgid "America/Cordoba" +msgstr "Châu Mỹ/Cordoba" + +#: ../calendar/zones.h:82 +msgid "America/Costa_Rica" +msgstr "Châu Mỹ/Costa_Rica" + +#: ../calendar/zones.h:83 +msgid "America/Cuiaba" +msgstr "Châu Mỹ/Cuiaba" + +#: ../calendar/zones.h:84 +msgid "America/Curacao" +msgstr "Châu Mỹ/Curacao" + +#: ../calendar/zones.h:85 +msgid "America/Danmarkshavn" +msgstr "Châu Mỹ/Danmarkshavn" + +#: ../calendar/zones.h:86 +msgid "America/Dawson" +msgstr "Châu Mỹ/Dawson" + +#: ../calendar/zones.h:87 +msgid "America/Dawson_Creek" +msgstr "Châu Mỹ/Dawson_Creek" + +#: ../calendar/zones.h:88 +msgid "America/Denver" +msgstr "Châu Mỹ/Denver" + +#: ../calendar/zones.h:89 +msgid "America/Detroit" +msgstr "Châu Mỹ/Detroit" + +#: ../calendar/zones.h:90 +msgid "America/Dominica" +msgstr "Châu Mỹ/Dominica" + +#: ../calendar/zones.h:91 +msgid "America/Edmonton" +msgstr "Châu Mỹ/Edmonton" + +#: ../calendar/zones.h:92 +msgid "America/Eirunepe" +msgstr "Châu Mỹ/Eirunepe" + +#: ../calendar/zones.h:93 +msgid "America/El_Salvador" +msgstr "Châu Mỹ/El_Salvador" + +#: ../calendar/zones.h:94 +msgid "America/Fortaleza" +msgstr "Châu Mỹ/Fortaleza" + +#: ../calendar/zones.h:95 +msgid "America/Glace_Bay" +msgstr "Châu Mỹ/Glace_Bay" + +#: ../calendar/zones.h:96 +msgid "America/Godthab" +msgstr "Châu Mỹ/Godthab" + +#: ../calendar/zones.h:97 +msgid "America/Goose_Bay" +msgstr "Châu Mỹ/Goose_Bay" + +#: ../calendar/zones.h:98 +msgid "America/Grand_Turk" +msgstr "Châu Mỹ/Grand_Turk" + +#: ../calendar/zones.h:99 +msgid "America/Grenada" +msgstr "Châu Mỹ/Grenada" + +#: ../calendar/zones.h:100 +msgid "America/Guadeloupe" +msgstr "Châu Mỹ/Guadeloupe" + +#: ../calendar/zones.h:101 +msgid "America/Guatemala" +msgstr "Châu Mỹ/Guatemala" + +#: ../calendar/zones.h:102 +msgid "America/Guayaquil" +msgstr "Châu Mỹ/Guayaquil" + +#: ../calendar/zones.h:103 +msgid "America/Guyana" +msgstr "Châu Mỹ/Guyana" + +#: ../calendar/zones.h:104 +msgid "America/Halifax" +msgstr "Châu Mỹ/Halifax" + +#: ../calendar/zones.h:105 +msgid "America/Havana" +msgstr "Châu Mỹ/Havana" + +#: ../calendar/zones.h:106 +msgid "America/Hermosillo" +msgstr "Châu Mỹ/Hermosillo" + +#: ../calendar/zones.h:107 +msgid "America/Indiana/Indianapolis" +msgstr "Châu Mỹ/Indiana/Indianapolis" + +#: ../calendar/zones.h:108 +msgid "America/Indiana/Knox" +msgstr "Châu Mỹ/Indiana/Knox" + +#: ../calendar/zones.h:109 +msgid "America/Indiana/Marengo" +msgstr "Châu Mỹ/Indiana/Marengo" + +#: ../calendar/zones.h:110 +msgid "America/Indiana/Vevay" +msgstr "Châu Mỹ/Indiana/Vevay" + +#: ../calendar/zones.h:111 +msgid "America/Indianapolis" +msgstr "Châu Mỹ/Indianapolis" + +#: ../calendar/zones.h:112 +msgid "America/Inuvik" +msgstr "Châu Mỹ/Inuvik" + +#: ../calendar/zones.h:113 +msgid "America/Iqaluit" +msgstr "Châu Mỹ/Iqaluit" + +#: ../calendar/zones.h:114 +msgid "America/Jamaica" +msgstr "Châu Mỹ/Cha-mê-ca" + +#: ../calendar/zones.h:115 +msgid "America/Jujuy" +msgstr "Châu Mỹ/Jujuy" + +#: ../calendar/zones.h:116 +msgid "America/Juneau" +msgstr "Châu Mỹ/Juneau" + +#: ../calendar/zones.h:117 +msgid "America/Kentucky/Louisville" +msgstr "Châu Mỹ/Kentucky/Louisville" + +#: ../calendar/zones.h:118 +msgid "America/Kentucky/Monticello" +msgstr "Châu Mỹ/Kentucky/Monticello" + +#: ../calendar/zones.h:119 +msgid "America/La_Paz" +msgstr "Châu Mỹ/La_Paz" + +#: ../calendar/zones.h:120 +msgid "America/Lima" +msgstr "Châu Mỹ/Li-ma" + +#: ../calendar/zones.h:121 +msgid "America/Los_Angeles" +msgstr "Châu Mỹ/Los_Angeles" + +#: ../calendar/zones.h:122 +msgid "America/Louisville" +msgstr "Châu Mỹ/Louisville" + +#: ../calendar/zones.h:123 +msgid "America/Maceio" +msgstr "Châu Mỹ/Maceio" + +#: ../calendar/zones.h:124 +msgid "America/Managua" +msgstr "Châu Mỹ/Managua" + +#: ../calendar/zones.h:125 +msgid "America/Manaus" +msgstr "Châu Mỹ/Manaus" + +#: ../calendar/zones.h:126 +msgid "America/Martinique" +msgstr "Châu Mỹ/Martinique" + +#: ../calendar/zones.h:127 +msgid "America/Mazatlan" +msgstr "Châu Mỹ/Mazatlan" + +#: ../calendar/zones.h:128 +msgid "America/Mendoza" +msgstr "Châu Mỹ/Mendoza" + +#: ../calendar/zones.h:129 +msgid "America/Menominee" +msgstr "Châu Mỹ/Menominee" + +#: ../calendar/zones.h:130 +msgid "America/Merida" +msgstr "Châu Mỹ/Merida" + +#: ../calendar/zones.h:131 +msgid "America/Mexico_City" +msgstr "Châu Mỹ/TP_Mexico" + +#: ../calendar/zones.h:132 +msgid "America/Miquelon" +msgstr "Châu Mỹ/Miquelon" + +#: ../calendar/zones.h:133 +msgid "America/Monterrey" +msgstr "Châu Mỹ/Monterrey" + +#: ../calendar/zones.h:134 +msgid "America/Montevideo" +msgstr "Châu Mỹ/Montevideo" + +#: ../calendar/zones.h:135 +msgid "America/Montreal" +msgstr "Châu Mỹ/Montréal" + +#: ../calendar/zones.h:136 +msgid "America/Montserrat" +msgstr "Châu Mỹ/Montserrat" + +#: ../calendar/zones.h:137 +msgid "America/Nassau" +msgstr "Châu Mỹ/Nassau" + +#: ../calendar/zones.h:138 +#: ../widgets/e-timezone-dialog/e-timezone-dialog.glade.h:4 +msgid "America/New_York" +msgstr "Châu Mỹ/New_York" + +#: ../calendar/zones.h:139 +msgid "America/Nipigon" +msgstr "Châu Mỹ/Nipigon" + +#: ../calendar/zones.h:140 +msgid "America/Nome" +msgstr "Châu Mỹ/Nome" + +#: ../calendar/zones.h:141 +msgid "America/Noronha" +msgstr "Châu Mỹ/Noronha" + +#: ../calendar/zones.h:142 +msgid "America/North_Dakota/Center" +msgstr "Châu Mỹ/North_Dakota/Center" + +#: ../calendar/zones.h:143 +msgid "America/Panama" +msgstr "Châu Mỹ/Panama" + +#: ../calendar/zones.h:144 +msgid "America/Pangnirtung" +msgstr "Châu Mỹ/Pangnirtung" + +#: ../calendar/zones.h:145 +msgid "America/Paramaribo" +msgstr "Châu Mỹ/Paramaribo" + +#: ../calendar/zones.h:146 +msgid "America/Phoenix" +msgstr "Châu Mỹ/Phoenix" + +#: ../calendar/zones.h:147 +msgid "America/Port-au-Prince" +msgstr "Châu Mỹ/Port-au-Prince" + +#: ../calendar/zones.h:148 +msgid "America/Port_of_Spain" +msgstr "Châu Mỹ/Port_of_Spain" + +#: ../calendar/zones.h:149 +msgid "America/Porto_Velho" +msgstr "Châu Mỹ/Porto_Velho" + +#: ../calendar/zones.h:150 +msgid "America/Puerto_Rico" +msgstr "Châu Mỹ/Puerto_Rico" + +#: ../calendar/zones.h:151 +msgid "America/Rainy_River" +msgstr "Châu Mỹ/Rainy_River" + +#: ../calendar/zones.h:152 +msgid "America/Rankin_Inlet" +msgstr "Châu Mỹ/Rankin_Inlet" + +#: ../calendar/zones.h:153 +msgid "America/Recife" +msgstr "Châu Mỹ/Recife" + +#: ../calendar/zones.h:154 +msgid "America/Regina" +msgstr "Châu Mỹ/Regina" + +#: ../calendar/zones.h:155 +msgid "America/Rio_Branco" +msgstr "Châu Mỹ/Rio_Branco" + +#: ../calendar/zones.h:156 +msgid "America/Rosario" +msgstr "Châu Mỹ/Rosario" + +#: ../calendar/zones.h:157 +msgid "America/Santiago" +msgstr "Châu Mỹ/Santiago" + +#: ../calendar/zones.h:158 +msgid "America/Santo_Domingo" +msgstr "Châu Mỹ/Santo_Domingo" + +#: ../calendar/zones.h:159 +msgid "America/Sao_Paulo" +msgstr "Châu Mỹ/Sao_Paulo" + +#: ../calendar/zones.h:160 +msgid "America/Scoresbysund" +msgstr "Châu Mỹ/Scoresbysund" + +#: ../calendar/zones.h:161 +msgid "America/Shiprock" +msgstr "Châu Mỹ/Shiprock" + +#: ../calendar/zones.h:162 +msgid "America/St_Johns" +msgstr "Châu Mỹ/St_Johns" + +#: ../calendar/zones.h:163 +msgid "America/St_Kitts" +msgstr "Châu Mỹ/St_Kitts" + +#: ../calendar/zones.h:164 +msgid "America/St_Lucia" +msgstr "Châu Mỹ/St_Lucia" + +#: ../calendar/zones.h:165 +msgid "America/St_Thomas" +msgstr "Châu Mỹ/St_Thomas" + +#: ../calendar/zones.h:166 +msgid "America/St_Vincent" +msgstr "Châu Mỹ/St_Vincent" + +#: ../calendar/zones.h:167 +msgid "America/Swift_Current" +msgstr "Châu Mỹ/Swift_Current" + +#: ../calendar/zones.h:168 +msgid "America/Tegucigalpa" +msgstr "Châu Mỹ/Tegucigalpa" + +#: ../calendar/zones.h:169 +msgid "America/Thule" +msgstr "Châu Mỹ/Thule" + +#: ../calendar/zones.h:170 +msgid "America/Thunder_Bay" +msgstr "Châu Mỹ/Thunder_Bay" + +#: ../calendar/zones.h:171 +msgid "America/Tijuana" +msgstr "Châu Mỹ/Tijuana" + +#: ../calendar/zones.h:172 +msgid "America/Tortola" +msgstr "Châu Mỹ/Tortola" + +#: ../calendar/zones.h:173 +msgid "America/Vancouver" +msgstr "Châu Mỹ/Vancouver" + +#: ../calendar/zones.h:174 +msgid "America/Whitehorse" +msgstr "Châu Mỹ/Whitehorse" + +#: ../calendar/zones.h:175 +msgid "America/Winnipeg" +msgstr "Châu Mỹ/Winnipeg" + +#: ../calendar/zones.h:176 +msgid "America/Yakutat" +msgstr "Châu Mỹ/Yakutat" + +#: ../calendar/zones.h:177 +msgid "America/Yellowknife" +msgstr "Châu Mỹ/Yellowknife" + +#: ../calendar/zones.h:178 +msgid "Antarctica/Casey" +msgstr "Nam Cực/Casey" + +#: ../calendar/zones.h:179 +msgid "Antarctica/Davis" +msgstr "Nam Cực/Davis" + +#: ../calendar/zones.h:180 +msgid "Antarctica/DumontDUrville" +msgstr "Nam Cực/DumontDUrville" + +#: ../calendar/zones.h:181 +msgid "Antarctica/Mawson" +msgstr "Nam Cực/Mawson" + +#: ../calendar/zones.h:182 +msgid "Antarctica/McMurdo" +msgstr "Nam Cực/McMurdo" + +#: ../calendar/zones.h:183 +msgid "Antarctica/Palmer" +msgstr "Nam Cực/Palmer" + +#: ../calendar/zones.h:184 +msgid "Antarctica/South_Pole" +msgstr "Nam Cực/South_Pole" + +#: ../calendar/zones.h:185 +msgid "Antarctica/Syowa" +msgstr "Nam Cực/Syowa" + +#: ../calendar/zones.h:186 +msgid "Antarctica/Vostok" +msgstr "Nam Cực/Vostok" + +#: ../calendar/zones.h:187 +msgid "Arctic/Longyearbyen" +msgstr "Arctic/Longyearbyen" + +#: ../calendar/zones.h:188 +msgid "Asia/Aden" +msgstr "Châu Á/Aden" + +#: ../calendar/zones.h:189 +msgid "Asia/Almaty" +msgstr "Châu Á/Almaty" + +#: ../calendar/zones.h:190 +msgid "Asia/Amman" +msgstr "Châu Á/Amman" + +#: ../calendar/zones.h:191 +msgid "Asia/Anadyr" +msgstr "Châu Á/Anadyr" + +#: ../calendar/zones.h:192 +msgid "Asia/Aqtau" +msgstr "Châu Á/Aqtau" + +#: ../calendar/zones.h:193 +msgid "Asia/Aqtobe" +msgstr "Châu Á/Aqtobe" + +#: ../calendar/zones.h:194 +msgid "Asia/Ashgabat" +msgstr "Châu Á/Ashgabat" + +#: ../calendar/zones.h:195 +msgid "Asia/Baghdad" +msgstr "Châu Á/Baghdad" + +#: ../calendar/zones.h:196 +msgid "Asia/Bahrain" +msgstr "Châu Á/Bahrain" + +#: ../calendar/zones.h:197 +msgid "Asia/Baku" +msgstr "Châu Á/Baku" + +#: ../calendar/zones.h:198 +msgid "Asia/Bangkok" +msgstr "Châu Á/Bangkok" + +#: ../calendar/zones.h:199 +msgid "Asia/Beirut" +msgstr "Châu Á/Beirut" + +#: ../calendar/zones.h:200 +msgid "Asia/Bishkek" +msgstr "Châu Á/Bishkek" + +#: ../calendar/zones.h:201 +msgid "Asia/Brunei" +msgstr "Châu Á/Bợ-ru-nei" + +#: ../calendar/zones.h:202 +msgid "Asia/Calcutta" +msgstr "Châu Á/Calcutta" + +#: ../calendar/zones.h:203 +msgid "Asia/Choibalsan" +msgstr "Châu Á/Choibalsan" + +#: ../calendar/zones.h:204 +msgid "Asia/Chongqing" +msgstr "Châu Á/Chongqing" + +#: ../calendar/zones.h:205 +msgid "Asia/Colombo" +msgstr "Châu Á/Colombo" + +#: ../calendar/zones.h:206 +msgid "Asia/Damascus" +msgstr "Châu Á/Damascus" + +#: ../calendar/zones.h:207 +msgid "Asia/Dhaka" +msgstr "Châu Á/Dhaka" + +#: ../calendar/zones.h:208 +msgid "Asia/Dili" +msgstr "Châu Á/Dili" + +#: ../calendar/zones.h:209 +msgid "Asia/Dubai" +msgstr "Châu Á/Dubai" + +#: ../calendar/zones.h:210 +msgid "Asia/Dushanbe" +msgstr "Châu Á/Dushanbe" + +#: ../calendar/zones.h:211 +msgid "Asia/Gaza" +msgstr "Châu Á/Gaza" + +#: ../calendar/zones.h:212 +msgid "Asia/Harbin" +msgstr "Châu Á/Harbin" + +#: ../calendar/zones.h:213 +msgid "Asia/Hong_Kong" +msgstr "Châu Á/Hồng_Kông" + +#: ../calendar/zones.h:214 +msgid "Asia/Hovd" +msgstr "Châu Á/Hovd" + +#: ../calendar/zones.h:215 +msgid "Asia/Irkutsk" +msgstr "Châu Á/Irkutsk" + +#: ../calendar/zones.h:216 +msgid "Asia/Istanbul" +msgstr "Châu Á/Istanbul" + +#: ../calendar/zones.h:217 +msgid "Asia/Jakarta" +msgstr "Châu Á/Jakarta" + +#: ../calendar/zones.h:218 +msgid "Asia/Jayapura" +msgstr "Châu Á/Jayapura" + +#: ../calendar/zones.h:219 +msgid "Asia/Jerusalem" +msgstr "Châu Á/Jerusalem" + +#: ../calendar/zones.h:220 +msgid "Asia/Kabul" +msgstr "Châu Á/Kabul" + +#: ../calendar/zones.h:221 +msgid "Asia/Kamchatka" +msgstr "Châu Á/Kamchatka" + +#: ../calendar/zones.h:222 +msgid "Asia/Karachi" +msgstr "Châu Á/Karachi" + +#: ../calendar/zones.h:223 +msgid "Asia/Kashgar" +msgstr "Châu Á/Kashgar" + +#: ../calendar/zones.h:224 +msgid "Asia/Katmandu" +msgstr "Châu Á/Katmandu" + +#: ../calendar/zones.h:225 +msgid "Asia/Krasnoyarsk" +msgstr "Châu Á/Krasnoyarsk" + +#: ../calendar/zones.h:226 +msgid "Asia/Kuala_Lumpur" +msgstr "Châu Á/Kuala_Lumpur" + +#: ../calendar/zones.h:227 +msgid "Asia/Kuching" +msgstr "Châu Á/Kuching" + +#: ../calendar/zones.h:228 +msgid "Asia/Kuwait" +msgstr "Châu Á/Cu-ouait" + +#: ../calendar/zones.h:229 +msgid "Asia/Macao" +msgstr "Châu Á/Macao" + +#: ../calendar/zones.h:230 +msgid "Asia/Macau" +msgstr "Châu Á/Ma-cao" + +#: ../calendar/zones.h:231 +msgid "Asia/Magadan" +msgstr "Châu Á/Magadan" + +#: ../calendar/zones.h:232 +msgid "Asia/Makassar" +msgstr "Châu Á/Makassar" + +#: ../calendar/zones.h:233 +msgid "Asia/Manila" +msgstr "Châu Á/Manila" + +#: ../calendar/zones.h:234 +msgid "Asia/Muscat" +msgstr "Châu Á/Muscat" + +#: ../calendar/zones.h:235 +msgid "Asia/Nicosia" +msgstr "Châu Á/Nicosia" + +#: ../calendar/zones.h:236 +msgid "Asia/Novosibirsk" +msgstr "Châu Á/Novosibirsk" + +#: ../calendar/zones.h:237 +msgid "Asia/Omsk" +msgstr "Châu Á/Omsk" + +#: ../calendar/zones.h:238 +msgid "Asia/Oral" +msgstr "Châu Á/Oral" + +#: ../calendar/zones.h:239 +msgid "Asia/Phnom_Penh" +msgstr "Châu Á/Phnom_Penh" + +#: ../calendar/zones.h:240 +msgid "Asia/Pontianak" +msgstr "Châu Á/Pontianak" + +#: ../calendar/zones.h:241 +msgid "Asia/Pyongyang" +msgstr "Châu Á/Pyongyang" + +#: ../calendar/zones.h:242 +msgid "Asia/Qatar" +msgstr "Châu Á/Qatar" + +#: ../calendar/zones.h:243 +msgid "Asia/Qyzylorda" +msgstr "Châu Á/Qyzylorda" + +#: ../calendar/zones.h:244 +msgid "Asia/Rangoon" +msgstr "Châu Á/Rangoon" + +#: ../calendar/zones.h:245 +msgid "Asia/Riyadh" +msgstr "Châu Á/Riyadh" + +#: ../calendar/zones.h:246 +msgid "Asia/Saigon" +msgstr "Châu Á/Sài_Gòn" + +#: ../calendar/zones.h:247 +msgid "Asia/Sakhalin" +msgstr "Châu Á/Sakhalin" + +#: ../calendar/zones.h:248 +msgid "Asia/Samarkand" +msgstr "Châu Á/Samarkand" + +#: ../calendar/zones.h:249 +msgid "Asia/Seoul" +msgstr "Châu Á/Seoul" + +#: ../calendar/zones.h:250 +msgid "Asia/Shanghai" +msgstr "Châu Á/Shanghai" + +#: ../calendar/zones.h:251 +msgid "Asia/Singapore" +msgstr "Châu Á/Xing-a-poa" + +#: ../calendar/zones.h:252 +msgid "Asia/Taipei" +msgstr "Châu Á/Tai-pei" + +#: ../calendar/zones.h:253 +msgid "Asia/Tashkent" +msgstr "Châu Á/Tashkent" + +#: ../calendar/zones.h:254 +msgid "Asia/Tbilisi" +msgstr "Châu Á/Tbilisi" + +#: ../calendar/zones.h:255 +msgid "Asia/Tehran" +msgstr "Châu Á/Tehran" + +#: ../calendar/zones.h:256 +msgid "Asia/Thimphu" +msgstr "Châu Á/Thimphu" + +#: ../calendar/zones.h:257 +msgid "Asia/Tokyo" +msgstr "Châu Á/Tokyo" + +#: ../calendar/zones.h:258 +msgid "Asia/Ujung_Pandang" +msgstr "Châu Á/Ujung_Pandang" + +#: ../calendar/zones.h:259 +msgid "Asia/Ulaanbaatar" +msgstr "Châu Á/Ulaanbaatar" + +#: ../calendar/zones.h:260 +msgid "Asia/Urumqi" +msgstr "Châu Á/Urumqi" + +#: ../calendar/zones.h:261 +msgid "Asia/Vientiane" +msgstr "Châu Á/Vientiane" + +#: ../calendar/zones.h:262 +msgid "Asia/Vladivostok" +msgstr "Châu Á/Vladivostok" + +#: ../calendar/zones.h:263 +msgid "Asia/Yakutsk" +msgstr "Châu Á/Yakutsk" + +#: ../calendar/zones.h:264 +msgid "Asia/Yekaterinburg" +msgstr "Châu Á/Yekaterinburg" + +#: ../calendar/zones.h:265 +msgid "Asia/Yerevan" +msgstr "Châu Á/Yerevan" + +#: ../calendar/zones.h:266 +msgid "Atlantic/Azores" +msgstr "Đại Tây Dương/Azores" + +#: ../calendar/zones.h:267 +msgid "Atlantic/Bermuda" +msgstr "Đại Tây Dương/Bermuda" + +#: ../calendar/zones.h:268 +msgid "Atlantic/Canary" +msgstr "Đại Tây Dương/Canary" + +#: ../calendar/zones.h:269 +msgid "Atlantic/Cape_Verde" +msgstr "Đại Tây Dương/Cape_Verde" + +#: ../calendar/zones.h:270 +msgid "Atlantic/Faeroe" +msgstr "Đại Tây Dương/Faeroe" + +#: ../calendar/zones.h:271 +msgid "Atlantic/Jan_Mayen" +msgstr "Đại Tây Dương/Jan_Mayen" + +#: ../calendar/zones.h:272 +msgid "Atlantic/Madeira" +msgstr "Đại Tây Dương/Madeira" + +#: ../calendar/zones.h:273 +msgid "Atlantic/Reykjavik" +msgstr "Đại Tây Dương/Reykjavik" + +#: ../calendar/zones.h:274 +msgid "Atlantic/South_Georgia" +msgstr "Đại Tây Dương/South_Georgia" + +#: ../calendar/zones.h:275 +msgid "Atlantic/St_Helena" +msgstr "Đại Tây Dương/St_Helena" + +#: ../calendar/zones.h:276 +msgid "Atlantic/Stanley" +msgstr "Đại Tây Dương/Stanley" + +#: ../calendar/zones.h:277 +msgid "Australia/Adelaide" +msgstr "Châu Úc/Adelaide" + +#: ../calendar/zones.h:278 +msgid "Australia/Brisbane" +msgstr "Châu Úc/Brisbane" + +#: ../calendar/zones.h:279 +msgid "Australia/Broken_Hill" +msgstr "Châu Úc/Broken_Hill" + +#: ../calendar/zones.h:280 +msgid "Australia/Darwin" +msgstr "Châu Úc/Darwin" + +#: ../calendar/zones.h:281 +msgid "Australia/Hobart" +msgstr "Châu Úc/Hobart" + +#: ../calendar/zones.h:282 +msgid "Australia/Lindeman" +msgstr "Châu Úc/Lindeman" + +#: ../calendar/zones.h:283 +msgid "Australia/Lord_Howe" +msgstr "Châu Úc/Lord_Howe" + +#: ../calendar/zones.h:284 +msgid "Australia/Melbourne" +msgstr "Châu Úc/Melbourne" + +#: ../calendar/zones.h:285 +msgid "Australia/Perth" +msgstr "Châu Úc/Perth" + +#: ../calendar/zones.h:286 +msgid "Australia/Sydney" +msgstr "Châu Úc/Sydney" + +#: ../calendar/zones.h:287 +msgid "Europe/Amsterdam" +msgstr "Châu Âu/Amsterdam" + +#: ../calendar/zones.h:288 +msgid "Europe/Andorra" +msgstr "Châu Âu/Andorra" + +#: ../calendar/zones.h:289 +msgid "Europe/Athens" +msgstr "Châu Âu/Athens" + +#: ../calendar/zones.h:290 +msgid "Europe/Belfast" +msgstr "Châu Âu/Belfast" + +#: ../calendar/zones.h:291 +msgid "Europe/Belgrade" +msgstr "Châu Âu/Belgrade" + +#: ../calendar/zones.h:292 +msgid "Europe/Berlin" +msgstr "Châu Âu/Berlin" + +#: ../calendar/zones.h:293 +msgid "Europe/Bratislava" +msgstr "Châu Âu/Bratislava" + +#: ../calendar/zones.h:294 +msgid "Europe/Brussels" +msgstr "Châu Âu/Brussels" + +#: ../calendar/zones.h:295 +msgid "Europe/Bucharest" +msgstr "Châu Âu/Bucharest" + +#: ../calendar/zones.h:296 +msgid "Europe/Budapest" +msgstr "Châu Âu/Budapest" + +#: ../calendar/zones.h:297 +msgid "Europe/Chisinau" +msgstr "Châu Âu/Chisinau" + +#: ../calendar/zones.h:298 +msgid "Europe/Copenhagen" +msgstr "Châu Âu/Copenhagen" + +#: ../calendar/zones.h:299 +msgid "Europe/Dublin" +msgstr "Châu Âu/Dublin" + +#: ../calendar/zones.h:300 +msgid "Europe/Gibraltar" +msgstr "Châu Âu/Gibraltar" + +#: ../calendar/zones.h:301 +msgid "Europe/Helsinki" +msgstr "Châu Âu/Helsinki" + +#: ../calendar/zones.h:302 +msgid "Europe/Istanbul" +msgstr "Châu Âu/Istanbul" + +#: ../calendar/zones.h:303 +msgid "Europe/Kaliningrad" +msgstr "Châu Âu/Kaliningrad" + +#: ../calendar/zones.h:304 +msgid "Europe/Kiev" +msgstr "Châu Âu/Kiev" + +#: ../calendar/zones.h:305 +msgid "Europe/Lisbon" +msgstr "Châu Âu/Lisbon" + +#: ../calendar/zones.h:306 +msgid "Europe/Ljubljana" +msgstr "Châu Âu/Ljubljana" + +#: ../calendar/zones.h:307 +msgid "Europe/London" +msgstr "Châu Âu/London" + +#: ../calendar/zones.h:308 +msgid "Europe/Luxembourg" +msgstr "Châu Âu/Luxembourg" + +#: ../calendar/zones.h:309 +msgid "Europe/Madrid" +msgstr "Châu Âu/Madrid" + +#: ../calendar/zones.h:310 +msgid "Europe/Malta" +msgstr "Châu Âu/Moa-ta" + +#: ../calendar/zones.h:311 +msgid "Europe/Minsk" +msgstr "Châu Âu/Minsk" + +#: ../calendar/zones.h:312 +msgid "Europe/Monaco" +msgstr "Châu Âu/Monaco" + +#: ../calendar/zones.h:313 +msgid "Europe/Moscow" +msgstr "Châu Âu/Moscow" + +#: ../calendar/zones.h:314 +msgid "Europe/Nicosia" +msgstr "Châu Âu/Nicosia" + +#: ../calendar/zones.h:315 +msgid "Europe/Oslo" +msgstr "Châu Âu/Oslo" + +#: ../calendar/zones.h:316 +msgid "Europe/Paris" +msgstr "Châu Âu/Paris" + +#: ../calendar/zones.h:317 +msgid "Europe/Prague" +msgstr "Châu Âu/Prague" + +#: ../calendar/zones.h:318 +msgid "Europe/Riga" +msgstr "Châu Âu/Riga" + +#: ../calendar/zones.h:319 +msgid "Europe/Rome" +msgstr "Châu Âu/Rome" + +#: ../calendar/zones.h:320 +msgid "Europe/Samara" +msgstr "Châu Âu/Samara" + +#: ../calendar/zones.h:321 +msgid "Europe/San_Marino" +msgstr "Châu Âu/San_Marino" + +#: ../calendar/zones.h:322 +msgid "Europe/Sarajevo" +msgstr "Châu Âu/Sarajevo" + +#: ../calendar/zones.h:323 +msgid "Europe/Simferopol" +msgstr "Châu Âu/Simferopol" + +#: ../calendar/zones.h:324 +msgid "Europe/Skopje" +msgstr "Châu Âu/Skopje" + +#: ../calendar/zones.h:325 +msgid "Europe/Sofia" +msgstr "Châu Âu/Sofia" + +#: ../calendar/zones.h:326 +msgid "Europe/Stockholm" +msgstr "Châu Âu/Stockholm" + +#: ../calendar/zones.h:327 +msgid "Europe/Tallinn" +msgstr "Châu Âu/Tallinn" + +#: ../calendar/zones.h:328 +msgid "Europe/Tirane" +msgstr "Châu Âu/Tirane" + +#: ../calendar/zones.h:329 +msgid "Europe/Uzhgorod" +msgstr "Châu Âu/Uzhgorod" + +#: ../calendar/zones.h:330 +msgid "Europe/Vaduz" +msgstr "Châu Âu/Vaduz" + +#: ../calendar/zones.h:331 +msgid "Europe/Vatican" +msgstr "Châu Âu/Vatican" + +#: ../calendar/zones.h:332 +msgid "Europe/Vienna" +msgstr "Châu Âu/Vienna" + +#: ../calendar/zones.h:333 +msgid "Europe/Vilnius" +msgstr "Châu Âu/Vilnius" + +#: ../calendar/zones.h:334 +msgid "Europe/Warsaw" +msgstr "Châu Âu/Warsaw" + +#: ../calendar/zones.h:335 +msgid "Europe/Zagreb" +msgstr "Châu Âu/Zagreb" + +#: ../calendar/zones.h:336 +msgid "Europe/Zaporozhye" +msgstr "Châu Âu/Zaporozhye" + +#: ../calendar/zones.h:337 +msgid "Europe/Zurich" +msgstr "Châu Âu/Zurich" + +#: ../calendar/zones.h:338 +msgid "Indian/Antananarivo" +msgstr "Indian/Antananarivo" + +#: ../calendar/zones.h:339 +msgid "Indian/Chagos" +msgstr "Ấn Độ Dương/Chagos" + +#: ../calendar/zones.h:340 +msgid "Indian/Christmas" +msgstr "Ấn Độ Dương/Christmas" + +#: ../calendar/zones.h:341 +msgid "Indian/Cocos" +msgstr "Ấn Độ Dương/Cocos" + +#: ../calendar/zones.h:342 +msgid "Indian/Comoro" +msgstr "Ấn Độ Dương/Comoro" + +#: ../calendar/zones.h:343 +msgid "Indian/Kerguelen" +msgstr "Ấn Độ Dương/Kerguelen" + +#: ../calendar/zones.h:344 +msgid "Indian/Mahe" +msgstr "Ấn Độ Dương/Mahe" + +#: ../calendar/zones.h:345 +msgid "Indian/Maldives" +msgstr "Ấn Độ Dương/Maldives" + +#: ../calendar/zones.h:346 +msgid "Indian/Mauritius" +msgstr "Ấn Độ Dương/Mauritius" + +#: ../calendar/zones.h:347 +msgid "Indian/Mayotte" +msgstr "Ấn Độ Dương/Mayotte" + +#: ../calendar/zones.h:348 +msgid "Indian/Reunion" +msgstr "Ấn Độ Dương/Reunion" + +#: ../calendar/zones.h:349 +msgid "Pacific/Apia" +msgstr "Thái Bình Dương/Apia" + +#: ../calendar/zones.h:350 +msgid "Pacific/Auckland" +msgstr "Thái Bình Dương/Auckland" + +#: ../calendar/zones.h:351 +msgid "Pacific/Chatham" +msgstr "Thái Bình Dương/Chatham" + +#: ../calendar/zones.h:352 +msgid "Pacific/Easter" +msgstr "Thái Bình Dương/Easter" + +#: ../calendar/zones.h:353 +msgid "Pacific/Efate" +msgstr "Thái Bình Dương/Efate" + +#: ../calendar/zones.h:354 +msgid "Pacific/Enderbury" +msgstr "Thái Bình Dương/Enderbury" + +#: ../calendar/zones.h:355 +msgid "Pacific/Fakaofo" +msgstr "Thái Bình Dương/Fakaofo" + +#: ../calendar/zones.h:356 +msgid "Pacific/Fiji" +msgstr "Thái Bình Dương/Phi-gi" + +#: ../calendar/zones.h:357 +msgid "Pacific/Funafuti" +msgstr "Thái Bình Dương/Funafuti" + +#: ../calendar/zones.h:358 +msgid "Pacific/Galapagos" +msgstr "Thái Bình Dương/Ga-la-pa-gos" + +#: ../calendar/zones.h:359 +msgid "Pacific/Gambier" +msgstr "Thái Bình Dương/Gambier" + +#: ../calendar/zones.h:360 +msgid "Pacific/Guadalcanal" +msgstr "Thái Bình Dương/Guadalcanal" + +#: ../calendar/zones.h:361 +msgid "Pacific/Guam" +msgstr "Thái Bình Dương/Guam" + +#: ../calendar/zones.h:362 +msgid "Pacific/Honolulu" +msgstr "Thái Bình Dương/Honolulu" + +#: ../calendar/zones.h:363 +msgid "Pacific/Johnston" +msgstr "Thái Bình Dương/Johnston" + +#: ../calendar/zones.h:364 +msgid "Pacific/Kiritimati" +msgstr "Thái Bình Dương/Kiritimati" + +#: ../calendar/zones.h:365 +msgid "Pacific/Kosrae" +msgstr "Thái Bình Dương/Kosrae" + +#: ../calendar/zones.h:366 +msgid "Pacific/Kwajalein" +msgstr "Thái Bình Dương/Kwajalein" + +#: ../calendar/zones.h:367 +msgid "Pacific/Majuro" +msgstr "Thái Bình Dương/Majuro" + +#: ../calendar/zones.h:368 +msgid "Pacific/Marquesas" +msgstr "Thái Bình Dương/Marquesas" + +#: ../calendar/zones.h:369 +msgid "Pacific/Midway" +msgstr "Thái Bình Dương/Midway" + +#: ../calendar/zones.h:370 +msgid "Pacific/Nauru" +msgstr "Thái Bình Dương/Nauru" + +#: ../calendar/zones.h:371 +msgid "Pacific/Niue" +msgstr "Thái Bình Dương/Niue" + +#: ../calendar/zones.h:372 +msgid "Pacific/Norfolk" +msgstr "Thái Bình Dương/Norfolk" + +#: ../calendar/zones.h:373 +msgid "Pacific/Noumea" +msgstr "Thái Bình Dương/Noumea" + +#: ../calendar/zones.h:374 +msgid "Pacific/Pago_Pago" +msgstr "Thái Bình Dương/Pago_Pago" + +#: ../calendar/zones.h:375 +msgid "Pacific/Palau" +msgstr "Thái Bình Dương/Palau" + +#: ../calendar/zones.h:376 +msgid "Pacific/Pitcairn" +msgstr "Thái Bình Dương/Pitcairn" + +#: ../calendar/zones.h:377 +msgid "Pacific/Ponape" +msgstr "Thái Bình Dương/Ponape" + +#: ../calendar/zones.h:378 +msgid "Pacific/Port_Moresby" +msgstr "Thái Bình Dương/Port_Moresby" + +#: ../calendar/zones.h:379 +msgid "Pacific/Rarotonga" +msgstr "Thái Bình Dương/Rarotonga" + +#: ../calendar/zones.h:380 +msgid "Pacific/Saipan" +msgstr "Thái Bình Dương/Sai-pan" + +#: ../calendar/zones.h:381 +msgid "Pacific/Tahiti" +msgstr "Thái Bình Dương/Ta-hi-ti" + +#: ../calendar/zones.h:382 +msgid "Pacific/Tarawa" +msgstr "Thái Bình Dương/Tarawa" + +#: ../calendar/zones.h:383 +msgid "Pacific/Tongatapu" +msgstr "Thái Bình Dương/Tongatapu" + +#: ../calendar/zones.h:384 +msgid "Pacific/Truk" +msgstr "Thái Bình Dương/Truk" + +#: ../calendar/zones.h:385 +msgid "Pacific/Wake" +msgstr "Thái Bình Dương/Wake" + +#: ../calendar/zones.h:386 +msgid "Pacific/Wallis" +msgstr "Thái Bình Dương/Wallis" + +#: ../calendar/zones.h:387 +msgid "Pacific/Yap" +msgstr "Thái Bình Dương/Yap" + +# Variable and unit: do not translate/ biến và đơn vị: đừng dịch +#: ../widgets/misc/e-attachment-bar.c:105 +#, c-format +msgid "%.0fK" +msgstr "%.0fK" + +#: ../widgets/misc/e-attachment-bar.c:108 +#, c-format +msgid "%.0fM" +msgstr "%.0fM" + +#: ../widgets/misc/e-attachment-bar.c:111 +#, c-format +msgid "%.0fG" +msgstr "%.0fG" + +#: ../widgets/misc/e-attachment-bar.c:908 +msgid "Attachment Bar" +msgstr "Thanh đính kèm" + +#: ../widgets/misc/e-attachment.c:420 ../widgets/misc/e-attachment.c:436 +#, c-format +msgid "Cannot attach file %s: %s" +msgstr "Không thể đính kèm tập tin « %s »: %s" + +#: ../widgets/misc/e-attachment.c:227 ../widgets/misc/e-attachment.c:428 +#, c-format +msgid "Cannot attach file %s: not a regular file" +msgstr "Không thể đính kèm tập tin « %s »: không phải tập tin bình thường." + +#: ../composer/e-msg-composer-hdrs.c:558 ../composer/e-msg-composer-hdrs.c:559 +msgid "Posting destination" +msgstr "Đích gởi đến" + +#: ../composer/e-msg-composer-hdrs.c:559 ../composer/e-msg-composer-hdrs.c:560 +msgid "Choose folders to post the message to." +msgstr "Hãy chọn các thư mục để gởi thư đó vào." + +#: ../composer/e-msg-composer-hdrs.c:593 ../composer/e-msg-composer-hdrs.c:594 +msgid "Click here for the address book" +msgstr "Nhấn vào đây để xem Sổ địa chỉ" + +#: ../composer/e-msg-composer-hdrs.c:623 ../composer/e-msg-composer-hdrs.c:624 +msgid "_Reply-To:" +msgstr "T_rả lời:" + +#: ../composer/e-msg-composer-hdrs.c:630 ../composer/e-msg-composer-hdrs.c:631 +msgid "Fr_om:" +msgstr "_Từ :" + +#: ../composer/e-msg-composer-hdrs.c:647 ../composer/e-msg-composer-hdrs.c:649 +msgid "Enter the recipients of the message" +msgstr "Nhập người nhận thư" + +#: ../composer/e-msg-composer-hdrs.c:651 ../composer/e-msg-composer-hdrs.c:653 +msgid "Enter the addresses that will receive a carbon copy of the message" +msgstr "Chép cho: hãy nhập các địa chỉ sẽ nhận một bản sao của thư đó." + +#: ../composer/e-msg-composer-hdrs.c:655 ../composer/e-msg-composer-hdrs.c:657 +msgid "" +"Enter the addresses that will receive a carbon copy of the message without " +"appearing in the recipient list of the message." +msgstr "" +"Bí mật Chép cho: hãy nhập các địa chỉ sẽ nhận một bản sao của thư mà không " +"xuất hiện tên trong danh sách người nhận (tránh người gởi thư rác ăn cấp các " +"địa chỉ đó nhé)." + +#: ../composer/e-msg-composer-hdrs.c:662 ../composer/e-msg-composer-hdrs.c:664 +msgid "_Post To:" +msgstr "_Gởi tới:" + +#: ../composer/e-msg-composer-hdrs.c:667 +msgid "Click here to select folders to post to" +msgstr "Nhấn vào đây để chọn thư mục gởi đến" + +#: ../composer/e-msg-composer-hdrs.c:673 ../composer/e-msg-composer-hdrs.c:675 +msgid "Post To:" +msgstr "Gởi tới:" + +#: ../composer/e-msg-composer-select-file.c:82 +#: ../composer/e-msg-composer-select-file.c:81 +msgid "A_ttach" +msgstr "Đính _kèm" + +#: ../composer/e-msg-composer-select-file.c:239 +msgid "Insert Attachment" +msgstr "Chèn đính kèm" + +#: ../composer/e-msg-composer.c:830 ../composer/e-msg-composer.c:738 +msgid "" +"Cannot sign outgoing message: No signing certificate set for this account" +msgstr "" +"Không thể ký tên thư gởi đi: chưa lập chứng nhận chữ ký cho tài khoản này." + +#: ../composer/e-msg-composer.c:837 ../composer/e-msg-composer.c:745 +msgid "" +"Cannot encrypt outgoing message: No encryption certificate set for this " +"account" +msgstr "" +"Không thể mật mã hóa thư gởi đi: chưa lập chứng nhận mật mã cho tài khoản " +"này." + +#: ../plug-ins/common/svg.c:315 ../plug-ins/common/svg.c:717 +msgid "Unknown reason" +msgstr "Không biết sao" + +#: ../composer/e-msg-composer.c:1392 ../composer/e-msg-composer.c:1293 +#: ../gmedia_slice/callbacks.c:739 +#, c-format +msgid "Could not open file" +msgstr "Không thể mở tập tin" + +#: ../composer/e-msg-composer.c:1400 ../composer/e-msg-composer.c:1301 +msgid "Unable to retrieve message from editor" +msgstr "Không nhận được thư từ trình biên soạn." + +#: ../composer/e-msg-composer.c:1678 ../composer/e-msg-composer.c:1571 +msgid "Untitled Message" +msgstr "Thư chưa tên" + +#: ../glade/gnome/gnomeapp.c:172 ../plug-ins/common/spheredesigner.c:2195 +#: ../glade/gnome/gnomeapp.c:173 ../src/gtkfunc.c:1203 ../scripts/test.c:278 +#: ../glade/pyblio.glade.in.h:9 +msgid "Open File" +msgstr "Mở tập tin" + +#: ../mail/em-account-editor.c:633 ../mail/em-account-editor.c:700 +msgid "Autogenerated" +msgstr "Tự động phát sinh" + +#: ../composer/e-msg-composer.c:2243 ../composer/e-msg-composer.c:2104 +msgid "Si_gnature:" +msgstr "Chữ _ký:" + +#: ../composer/e-msg-composer.c:3525 ../composer/e-msg-composer.c:3526 +msgid "Compose a message" +msgstr "Biên soạn thư" + +#: ../composer/e-msg-composer.c:3819 ../composer/e-msg-composer.c:3641 +msgid "_Attachment Bar" +msgstr "Thanh đính _kèm" + +#: ../composer/e-msg-composer.c:4911 ../composer/e-msg-composer.c:4716 +msgid "" +"(The composer contains a non-text message body, which cannot be edited.)" +"" +msgstr "" +"(Bộ soạn thảo chứa phần thân thư phi văn bản nên không thể hiệu chỉnh nó)" +"" + +#: ../composer/mail-composer.error.xml.h:1 +msgid "" +" There are few attachments getting downloaded. Sending the mail will cause " +"the mail to be sent without those pending attachments " +msgstr "" +"Hiện thời đang tải một số đính kèm về. Gởi thư này sẽ gởi nó không có những " +"đính kèm treo." + +#: ../composer/mail-composer.error.xml.h:2 +msgid "All accounts have been removed." +msgstr "Mọi tài khoản đã được gỡ bỏ." + +#: ../composer/mail-composer.error.xml.h:3 +msgid "" +"Are you sure you want to discard the message, titled '{0}', you are " +"composing?" +msgstr "Bạn có chắc muốn xoá bỏ thư tên « {0} » mà bạn đang soạn không?" + +#: ../composer/mail-composer.error.xml.h:4 +msgid "Because "{0}", you may need to select different mail options." +msgstr "Vì « {0} », có lẽ bạn cần chọn một số tùy chọn thư khác." + +#: ../composer/mail-composer.error.xml.h:5 ../e-util/e-system.error.xml.h:1 +#: ../mail/mail.error.xml.h:18 ../mail/mail.error.xml.h:17 +msgid "Because "{1}"." +msgstr "Vì « {1}»." + +#: ../composer/mail-composer.error.xml.h:6 +msgid "" +"Closing this composer window will discard the message permanently, unless " +"you choose to save the message in your Drafts folder. This will allow you to " +"continue the message at a later date." +msgstr "" +"Đóng cửa sổ soạn này thì sẽ xoá bỏ thư đó hoàn toàn, trừ bạn chọn lưu thư đó " +"vào thư mục Nháp. Làm như thế sẽ cho phép bạn tiếp tục thư đó lần sau." + +#: ../composer/mail-composer.error.xml.h:7 +msgid "Could not create composer window." +msgstr "Không thể tạo cửa sổ soạn." + +#: ../composer/mail-composer.error.xml.h:8 +msgid "Could not create message." +msgstr "Không thể tạo thư." + +#: ../composer/mail-composer.error.xml.h:9 +msgid "Could not read signature file "{0}"." +msgstr "Không thể đọc tập tin chữ ký « {0} »." + +#: ../composer/mail-composer.error.xml.h:10 +msgid "Could not retrieve messages to attach from {0}." +msgstr "Không thể gọi thư để đính kèm từ {0}." + +#: ../composer/mail-composer.error.xml.h:11 +msgid "Could not save to autosave file "{0}"." +msgstr "Không thể lưu vào tập tin lưu tự động « {0}»." + +#: ../composer/mail-composer.error.xml.h:12 +msgid "Directories can not be attached to Messages." +msgstr "Không thể đính thư mục kèm thư." + +#: ../composer/mail-composer.error.xml.h:13 +msgid "Do you want to recover unfinished messages?" +msgstr "Bạn có muốn phục hồi các thư chưa hoàn tất không?" + +#: ../composer/mail-composer.error.xml.h:14 +msgid "Don't Recover" +msgstr "Không phục hồi" + +#: ../composer/mail-composer.error.xml.h:15 +msgid "Download in progress. Do you want to send the mail?" +msgstr "Đang tải về. Bạn còn muốn gởi thư sao?" + +#: ../composer/mail-composer.error.xml.h:16 +msgid "Error saving to autosave because "{1}"." +msgstr "Gặp lỗi khi lưu vào tập tin lưu tự động vì « {1} »." + +#: ../composer/mail-composer.error.xml.h:17 +msgid "" +"Evolution quit unexpectedly while you were composing a new message. " +"Recovering the message will allow you to continue where you left off." +msgstr "" +"Trình Evolution đã thoát bất ngờ trong khi bạn soạn một thư mới. Phục hồi " +"thư đó thì sẽ cho phép bạn tiếp tục từ chỗ đó." + +#: ../composer/mail-composer.error.xml.h:18 +msgid "Recover" +msgstr "Phục hồi" + +#: ../composer/mail-composer.error.xml.h:19 +msgid "The file `{0}' is not a regular file and cannot be sent in a message." +msgstr "" +"Tập tin « {0} » không phải là tập tin chuẩn nên không thể gởi nó trong thư." + +#: ../composer/mail-composer.error.xml.h:20 +msgid "" +"To attach the contents of this directory, either attach the files in this " +"directory individually, or create an archive of the directory and attach it." +msgstr "" +"Để đính kèm nội dung thư mục này thì bạn hãy hoặc đính kèm mỗi tập tin trong " +"nó từng cái một, hoặc tạo một kho của toàn bộ thư mục và đính kèm kho đó." + +#: ../composer/mail-composer.error.xml.h:21 +msgid "" +"Unable to activate the HTML editor control.\n" +"\n" +"Please make sure that you have the correct version of gtkhtml and libgtkhtml " +"installed." +msgstr "" +"Không thể kích hoạt điều khiển bộ biên soạn HTML.\n" +"\n" +"Vui lòng kiểm tra xem GtkHTML và libGtkHTML có được cài đặt đúng phiên bản " +"chưa." + +#: ../composer/mail-composer.error.xml.h:24 +msgid "Unable to activate the address selector control." +msgstr "Không thể kích hoạt điều khiển bộ chọn địa chỉ." + +#: ../composer/mail-composer.error.xml.h:25 +msgid "Unfinished messages found" +msgstr "Tìm thấy thư chưa hoàn tất" + +#: ../composer/mail-composer.error.xml.h:26 +msgid "Warning: Modified Message" +msgstr "Cảnh báo : thư được sửa đổi." + +#: ../composer/mail-composer.error.xml.h:27 +msgid "You cannot attach the file `{0}' to this message." +msgstr "Không thể đính kèm tập tin « {0} » vào thư này." + +#: ../composer/mail-composer.error.xml.h:28 +msgid "You need to configure an account before you can compose mail." +msgstr "Bạn cần cấu hình một tài khoản nào đó trước khi có thể biên soạn thư." + +#: ../composer/mail-composer.error.xml.h:30 +msgid "_Save Message" +msgstr "_Lưu thư" + +# Name: do not translate/ tên: đừng dịch +#: ../shell/main.c:514 ../shell/main.c:509 +msgid "Evolution" +msgstr "Evolution" + +#: ../data/evolution.desktop.in.in.h:2 +msgid "The Evolution Groupware Suite" +msgstr "Bộ phần mềm nhóm Evolution" + +#: ../data/evolution.keys.in.in.h:1 +msgid "address card" +msgstr "thẻ địa chỉ" + +#: ../data/evolution.keys.in.in.h:2 +msgid "calendar information" +msgstr "thông tin lịch" + +#: ../designs/OOA/ooa.glade.h:1 ../storage/exchange-oof.glade.h:1 +msgid "" +"Currently, your status is \"Out of the Office\". \n" +"\n" +"Would you like to change your status to \"In the Office\"? " +msgstr "" +"Hiện thời, trạng thái của bạn là « Ngoài văn phòng ». \n" +"\n" +"Bạn có muốn thay đổi trạng thái thành « Trong văn phòng » không? " + +#: ../designs/OOA/ooa.glade.h:4 ../storage/exchange-oof.glade.h:4 +msgid "Out of Office Message:" +msgstr "Thông điệp Ngoài Văn Phòng:" + +#: ../designs/OOA/ooa.glade.h:5 ../storage/exchange-oof.glade.h:5 +#: ../data/UpdateManager.glade.h:2 ui/galeon.glade.h:6 +msgid "Status:" +msgstr "Trạng thái:" + +#: ../designs/OOA/ooa.glade.h:6 ../storage/exchange-oof.glade.h:6 +msgid "" +"The message specified below will be automatically sent to each person " +"who sends\n" +"mail to you while you are out of the office." +msgstr "" +"Thông điệp dưới đây sẽ được tự động gởi tới mỗi người gởi thư cho " +"bạn\n" +"khi bạn ở ngoài văn phòng." + +#: ../designs/OOA/ooa.glade.h:8 ../storage/exchange-oof.glade.h:8 +msgid "I am currently in the office" +msgstr "Tôi hiện thời ở trong văn phòng" + +#: ../designs/OOA/ooa.glade.h:9 ../storage/exchange-oof.glade.h:9 +msgid "I am currently out of the office" +msgstr "Tôi hiện thời ở ngoài văn phòng" + +#: ../designs/OOA/ooa.glade.h:10 ../storage/exchange-oof.glade.h:10 +msgid "No, Don't Change Status" +msgstr "Không, đừng thay đổi trạng thái" + +#: ../designs/OOA/ooa.glade.h:12 ../storage/exchange-oof.glade.h:11 +msgid "Out of Office Assistant" +msgstr "Trợ tá Ngoài Văn Phòng" + +#: ../designs/OOA/ooa.glade.h:13 ../storage/exchange-oof.glade.h:12 +msgid "Yes, Change Status" +msgstr "Có, thay đổi trạng thái" + +#: ../files/sharing-properties-view.glade.h:1 +msgid " " +msgstr " " + +#: ../designs/read_receipts/read.glade.h:2 +msgid "Receiving Email" +msgstr "Nhận thư" + +#: ../designs/read_receipts/read.glade.h:3 +msgid "Sending Email:" +msgstr "Gởi thư" + +#: ../designs/read_receipts/read.glade.h:4 +msgid "" +"This page allows you to choose if you want to be notified via a read " +"receipt when a message you\n" +"sent is read, and to specify what Evolution should do when someone requests " +"a receipt from you." +msgstr "" +"Trang này cho phép bạn chọn nếu muốn nhận thông báo người nhận đã đọc " +"thư của bạn, và cũng có thể lập hành động của trình Evolution khi người khác " +"yêu cầu nhận thông báo đã đọc từ bạn." + +#: ../designs/read_receipts/read.glade.h:6 +msgid "Always send back a read receipt" +msgstr "Luôn luôn trả gởi một thông báo đã đọc" + +#: ../designs/read_receipts/read.glade.h:7 +msgid "Ask me if I want to send back a read receipt" +msgstr "Hỏi tôi nếu muốn trả gởi một thông báo đã đọc" + +#: ../designs/read_receipts/read.glade.h:8 +msgid "Never send back a read receipt" +msgstr "Không bao giờ trả gởi một thông báo đã đọc" + +#: ../designs/read_receipts/read.glade.h:9 +msgid "Read Receipts" +msgstr "Thông báo đã đọc" + +#: ../designs/read_receipts/read.glade.h:10 +msgid "Request a read receipt for all messages I send" +msgstr "Yêu cầu một thông báo đã đọc cho mọi thư tôi gởi" + +#: ../designs/read_receipts/read.glade.h:11 +msgid "Unless the message is sent to a mailing list, and not to me personally" +msgstr "Trừ khi gởi thư đó cho hộp thư chung, không phải cho tôi riêng" + +#: ../designs/read_receipts/read.glade.h:12 +msgid "" +"When you receive an email with a read receipt request, what should Evolution " +"do?" +msgstr "" +"Khi bạn nhận một thư yêu cầu nhận thông báo đã đọc, trình Evolution nên làm " +"gì vậy?" + +#: ../e-util/e-dialog-utils.c:281 ../e-util/e-dialog-utils.c:267 +msgid "" +"A file by that name already exists.\n" +"Overwrite it?" +msgstr "" +"Tập tin tên này đã có.\n" +"Ghi đè lên nó không?" + +#: ../e-util/e-dialog-utils.c:283 ../e-util/e-system.error.xml.h:6 +#: ../e-util/e-dialog-utils.c:269 ../src/totem-playlist.c:884 +msgid "Overwrite file?" +msgstr "Ghi đè lên tập tin không?" + +#: ../e-util/e-error.c:84 ../e-util/e-error.c:85 ../e-util/e-error.c:127 +msgid "Evolution Error" +msgstr "Lỗi Evolution" + +#: ../e-util/e-error.c:86 ../e-util/e-error.c:87 ../e-util/e-error.c:125 +msgid "Evolution Warning" +msgstr "Cảnh báo Evolution" + +#: ../e-util/e-error.c:124 +msgid "Evolution Information" +msgstr "Thông tin Evolution" + +#: ../e-util/e-error.c:126 +msgid "Evolution Query" +msgstr "Truy vấn Evolution" + +#. setup a dummy error +#: ../e-util/e-error.c:442 ../e-util/e-error.c:438 +#, c-format +msgid "" +"Internal error, unknown error '%s' requested" +msgstr "Lỗi nội tại, lỗi lạ « %s » được yêu cầu" + +#: ../e-util/e-system.error.xml.h:2 +msgid "Cannot open file "{0}"." +msgstr "Không thể mở tập tin « {0} »." + +#: ../e-util/e-system.error.xml.h:3 +msgid "Cannot save file "{0}"." +msgstr "Không thể lưu tập tin « {0} »." + +#: ../e-util/e-system.error.xml.h:4 +msgid "Do you wish to overwrite it?" +msgstr "Bạn có muốn ghi đè lên nó không?" + +#: ../e-util/e-system.error.xml.h:5 +msgid "File exists "{0}"." +msgstr "Tập tin « {0} » đã có." + +#: ../nautilus-cd-burner.c:868 ../src/sj-extracting.c:212 +#: ../data/glade/OverwriteDialog.glade.h:2 +msgid "_Overwrite" +msgstr "_Ghi đè" + +#: ../filter/filter-datespec.c:78 +#, c-format +msgid "1 second ago" +msgid_plural "%d seconds ago" +msgstr[0] "%d giây trước" + +#: ../filter/filter-datespec.c:79 +#, c-format +msgid "1 minute ago" +msgid_plural "%d minutes ago" +msgstr[0] "%d phút trước" + +#: ../filter/filter-datespec.c:80 +#, c-format +msgid "1 hour ago" +msgid_plural "%d hours ago" +msgstr[0] "%d giờ trước" + +#: ../filter/filter-datespec.c:81 +#, c-format +msgid "1 day ago" +msgid_plural "%d days ago" +msgstr[0] "%d ngày trước" + +#: ../filter/filter-datespec.c:82 +#, c-format +msgid "1 week ago" +msgid_plural "%d weeks ago" +msgstr[0] "%d tuần trước" + +#: ../filter/filter-datespec.c:83 +#, c-format +msgid "1 month ago" +msgid_plural "%d months ago" +msgstr[0] "%d tháng trước" + +#: ../filter/filter-datespec.c:84 +#, c-format +msgid "1 year ago" +msgid_plural "%d years ago" +msgstr[0] "%d năm trước" + +#: ../filter/filter-datespec.c:285 +msgid "" +msgstr "" + +#: ../filter/filter-datespec.c:288 ../filter/filter-datespec.c:299 +msgid "now" +msgstr "bây giờ" + +#. strftime for date filter display, only needs to show a day date (i.e. no time) +#: ../filter/filter-datespec.c:295 +msgid "%d-%b-%Y" +msgstr "%d-%b-%Y" + +#: ../filter/filter-datespec.c:415 +msgid "Select a time to compare against" +msgstr "Chọn thời điểm để đối chiếu" + +#: ../libgnomedb/handlers/plugins/gnome-db-entry-filesel.c:199 +msgid "Choose a file" +msgstr "Chọn tập tin" + +#: dselect/pkgdisplay.cc:61 +msgid "Important" +msgstr "Quan trọng" + +#: ../mail/em-migrate.c:1044 ../mail/mail-config.c:79 +msgid "To Do" +msgstr "Cần làm" + +#: ../mail/mail-config.glade.h:95 +msgid "Later" +msgstr "Sau đó" + +#: sound/sound.c:329 ../glom/glom.glade.h:149 +msgid "Test" +msgstr "Thử tra" + +#: ../filter/filter-rule.c:791 +msgid "_Search name:" +msgstr "Tên tìm _kiếm:" + +#: ../filter/filter-rule.c:819 +msgid "Find items that meet the following criteria" +msgstr "Tìm mục khớp tiêu chuẩn theo đây" + +#: ../filter/filter-rule.c:858 +msgid "If all criteria are met" +msgstr "Nếu mọi tiêu chuẩn đều thỏa" + +#: ../filter/filter-rule.c:858 +msgid "If any criteria are met" +msgstr "Nếu tiêu chuẩn nào thỏa" + +#: ../filter/filter-rule.c:860 +msgid "Find items:" +msgstr "Tìm mục:" + +#: ../filter/filter-rule.c:881 +msgid "All related" +msgstr "Mọi thứ liên quan" + +#: ../filter/filter-rule.c:881 +msgid "Replies" +msgstr "Trả lời" + +#: ../filter/filter-rule.c:881 +msgid "Replies and parents" +msgstr "Trả lời và mẹ" + +#: ../filter/filter-rule.c:883 +msgid "Include threads" +msgstr "Gồm các mạch" + +#: ../filter/filter.error.xml.h:1 +msgid "Bad regular expression "{0}"." +msgstr "Biểu thức chính quy sai « {0} »." + +#: ../filter/filter.error.xml.h:2 +msgid "Could not compile regular expression "{1}"." +msgstr "Không thể biên dịch biểu thức chính quy « {1} »." + +#: ../filter/filter.error.xml.h:3 +msgid "File "{0}" does not exist or is not a regular file." +msgstr "" +"Tập tin « {0} » không tồn tại hoặc không phải là một tập tin bình thường." + +#: ../filter/filter.error.xml.h:4 +msgid "Missing date." +msgstr "Thiếu ngày." + +#: ../filter/filter.error.xml.h:5 +msgid "Missing file name." +msgstr "Thiếu tên tập tin." + +#: ../filter/filter.error.xml.h:6 ../mail/mail.error.xml.h:67 +#: ../mail/mail.error.xml.h:65 +msgid "Missing name." +msgstr "Thiếu tên." + +#: ../filter/filter.error.xml.h:7 +msgid "Name "{0}" already used." +msgstr "Tên « {0} » đã được dùng." + +#: ../filter/filter.error.xml.h:8 +msgid "Please choose another name." +msgstr "Hãy chọn tên khác." + +#: ../filter/filter.error.xml.h:9 +msgid "You must choose a date." +msgstr "Bạn phải chọn ngày." + +#: ../filter/filter.error.xml.h:10 +msgid "You must name this filter." +msgstr "Bạn phải đặt tên cho bộ lọc này." + +#: ../filter/filter.error.xml.h:11 prog/aspell.cpp:965 +msgid "You must specify a file name." +msgstr "Bạn phải xác định tên tập tin." + +#: ../filter/filter.glade.h:1 +msgid "_Filter Rules" +msgstr "Quy tắc bộ _lọc" + +#: ../filter/filter.glade.h:2 +msgid "Compare against" +msgstr "Đối chiếu với" + +#: ../filter/filter.glade.h:4 +msgid "Show filters for mail:" +msgstr "Hiển thị bộ lọc cho thư :" + +#: ../filter/filter.glade.h:5 +msgid "" +"The message's date will be compared against\n" +"12:00am of the date specified." +msgstr "" +"Ngày gởi thư sẽ được đối chiếu với thời\n" +" điểm 12:00am tại ngày xác định." + +#: ../filter/filter.glade.h:7 +msgid "" +"The message's date will be compared against\n" +"a time relative to when filtering occurs." +msgstr "" +"Ngày gởi thư sẽ được đối chiếu\n" +"với thời điểm liên quan lúc lọc." + +#: ../filter/filter.glade.h:9 +msgid "" +"The message's date will be compared against\n" +"the current time when filtering occurs." +msgstr "" +"Ngày gởi thông điệp sẽ được đối chiếu\n" +"với thời điểm hiện thời, khi lọc." + +#: ../filter/filter.glade.h:12 +msgid "a time relative to the current time" +msgstr "thời điểm so với hiện thời" + +#: ../filter/filter.glade.h:13 +msgid "ago" +msgstr "trước" + +#: ../src/smart-playlist-dialog.c:164 +msgid "months" +msgstr "tháng" + +#: ../filter/filter.glade.h:19 +msgid "the current time" +msgstr "thời điểm hiện thời" + +#: ../filter/filter.glade.h:20 +msgid "the time you specify" +msgstr "thời điểm bạn chọn" + +#: ../filter/filter.glade.h:22 event-ui.c:1028 +msgid "years" +msgstr "năm" + +#: ../filter/rule-editor.c:292 ../filter/rule-editor.c:290 +msgid "Add Rule" +msgstr "Thêm quy tắc" + +#: ../filter/rule-editor.c:368 ../filter/rule-editor.c:366 +msgid "Edit Rule" +msgstr "Sửa quy tắc" + +#: ../filter/rule-editor.c:735 ../filter/rule-editor.c:698 +msgid "Rule name" +msgstr "Tên quy tắc" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:1 +msgid "Composer Preferences" +msgstr "Tùy thích bộ soạn thảo" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:2 +msgid "" +"Configure mail preferences, including security and message display, here" +msgstr "" +"Cấu hình Tùy thích thư tín, bao gồm tính bảo mật và cách hiển thị thư, ở đây." + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:3 +msgid "Configure spell-checking, signatures, and the message composer here" +msgstr "Cấu hình kiểm tra chính tả, chữ ký, và bộ soạn thảo thư ở đây" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:4 +msgid "Configure your email accounts here" +msgstr "Cấu hình tài khoản thư ở đây" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:5 +msgid "Evolution Mail" +msgstr "Thư tín Evolution" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:6 +msgid "Evolution Mail accounts configuration control" +msgstr "Điều khiển cấu hình tài khoản thư tín Evolution." + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:7 +msgid "Evolution Mail component" +msgstr "Thành phần thư tín Evolution" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:8 +msgid "Evolution Mail composer" +msgstr "Bộ soạn thư của Evolution" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:9 +msgid "Evolution Mail composer configuration control" +msgstr "Điều khiển cấu hình bộ soạn thư Evolution." + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:10 +msgid "Evolution Mail preferences control" +msgstr "Điều khiển sở thích thư tín Evolution." + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:12 +msgid "Mail Accounts" +msgstr "Tài khoản thư" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:13 +#: ../mail/mail-config.glade.h:97 +msgid "Mail Preferences" +msgstr "Tùy thích thư" + +#: ../mail/GNOME_Evolution_Mail.server.in.in.h:14 +#: ../gncal/gnomecal-prefs.c:1534 +msgid "_Mail" +msgstr "Th_ư tín" + +#: ../mail/em-account-editor.c:395 ../mail/em-account-editor.c:387 +#, c-format +msgid "%s License Agreement" +msgstr "Điều kiện quyền của « %s »" + +#: ../mail/em-account-editor.c:402 ../mail/em-account-editor.c:394 +#, c-format +msgid "" +"\n" +"Please read carefully the license agreement\n" +"for %s displayed below\n" +"and tick the check box for accepting it\n" +msgstr "" +"\n" +"Vui lòng đọc cẳn thận điều kiện quyền\n" +"cho « %s » bên dưới, và đánh dấu\n" +"trong hộp chọn để chấp nhận các điều kiện này.\n" + +#: ../src/ImportCommand.cs:27 ../libgimpwidgets/gimpfileentry.c:351 +#: ../widgets/gtk+.xml.in.h:159 +msgid "Select Folder" +msgstr "Chọn thư mục" + +#: ../mail/em-account-editor.c:768 ../mail/em-account-editor.c:760 +msgid "Ask for each message" +msgstr "Hỏi cho mỗi thư" + +#: ../mail/em-account-editor.c:1796 ../mail/mail-config.glade.h:116 +#: ../mail/em-account-editor.c:1760 ../mail/mail-config.glade.h:117 +msgid "Receiving Email" +msgstr "Đang nhận thư" + +#: ../mail/em-account-editor.c:1979 ../mail/em-account-editor.c:1943 +msgid "Automatically check for _new mail every" +msgstr "Tự động kiểm tra thư _mới mỗi" + +#: ../mail/em-account-editor.c:2160 ../mail/mail-config.glade.h:128 +#: ../mail/em-account-editor.c:2111 +msgid "Sending Email" +msgstr "Đang gởi thư" + +#: ../mail/em-account-editor.c:2219 ../mail/mail-config.glade.h:68 +#: ap-gl/ap-gl.c:63 src/ap-config.c:81 ../mail/em-account-editor.c:2162 +#: ../memprof.glade.h:16 +msgid "Defaults" +msgstr "Mặc định" + +#: ../mail/em-account-editor.c:2258 ../mail/em-account-editor.c:2332 +msgid "Receiving Options" +msgstr "Tùy chọn nhận" + +#: ../camel/providers/imap/camel-imap-provider.c:44 +msgid "Checking for New Mail" +msgstr "Kiểm tra tìm thư mới" + +#: ../mail/em-account-editor.c:2737 ../mail/mail-config.glade.h:34 +#: ../mail/em-account-editor.c:2657 +msgid "Account Editor" +msgstr "Bộ hiệu chỉnh tài khoản" + +#: ../mail/em-account-editor.c:2737 ../mail/mail-config.glade.h:80 +#: ../mail/em-account-editor.c:2657 +msgid "Evolution Account Assistant" +msgstr "Phụ tá tài khoản Evolution" + +#: ../objects/FS/function.c:968 +msgid "Disable" +msgstr "Tắt" + +#: src/dictmanagedlg.cpp:483 +msgid "Enable" +msgstr "Bật" + +#: ../src/boards/python/admin/profile_list.py:316 +msgid "[Default]" +msgstr "[Mặc định]" + +#: ../extensions/extensions-manager-ui/extensions-manager-ui.c:340 +#: ../src/glade-property.c:491 +msgid "Enabled" +msgstr "Hoạt động" + +#: ../mail/em-account-prefs.c:511 ../mail/em-account-prefs.c:510 +msgid "Account name" +msgstr "Tên tài khoản" + +#: ../src/netstat.c:443 ../gnome-netinfo/netstat.c:403 +msgid "Protocol" +msgstr "Giao thức" + +#: ../mail/em-account-prefs.c:518 ../mail/em-account-prefs.c:517 +msgid "Mail Accounts Table" +msgstr "Bảng tài khoản thư" + +#: ../plug-ins/common/gbr.c:425 ../plug-ins/common/gih.c:499 +#: ../plug-ins/common/gih.c:1158 ../plug-ins/gflare/gflare.c:2993 +msgid "Unnamed" +msgstr "Không tên" + +#: ../mail/em-composer-prefs.c:936 ../mail/em-composer-prefs.c:895 +msgid "Language(s)" +msgstr "Ngôn ngữ" + +#: ../mail/em-composer-prefs.c:979 ../mail/em-composer-prefs.c:938 +msgid "Add signature script" +msgstr "Thêm tập lệnh chữ ký" + +#: ../mail/em-composer-prefs.c:999 ../mail/em-composer-prefs.c:958 +msgid "Signature(s)" +msgstr "Chữ ký" + +#: ../mail/em-composer-utils.c:862 ../mail/em-format-quote.c:389 +#: ../mail/em-composer-utils.c:853 +msgid "-------- Forwarded Message --------" +msgstr "━━━Thư đã chuyển tiếp━━━" + +#: ../mail/em-composer-utils.c:1657 ../mail/em-composer-utils.c:1648 +msgid "an unknown sender" +msgstr "không biết người gởi đó" + +#. Note to translators: this is the attribution string used when quoting messages. +#. * each ${Variable} gets replaced with a value. To see a full list of available +#. * variables, see em-composer-utils.c:1514 +#: ../mail/em-composer-utils.c:1704 ../mail/em-composer-utils.c:1695 +msgid "" +"On ${AbbrevWeekdayName}, ${Year}-${Month}-${Day} at ${24Hour}:${Minute} " +"${TimeZone}, ${Sender} wrote:" +msgstr "" +"Vào ${AbbrevWeekdayName}, ngày ${Day}, tháng ${Month} năm ${Year} lúc " +"${24Hour}:${Minute} ${TimeZone}, ${Sender} viết:" + +#: ../mail/em-filter-editor.c:155 ../mail/em-filter-editor.c:147 +msgid "_Filter Rules" +msgstr "_Quy tắc bộ lọc" + +#. Automatically generated. Do not edit. +#: ../mail/em-filter-i18n.h:2 +msgid "Adjust Score" +msgstr "Chỉnh điểm" + +#: ../mail/em-filter-i18n.h:3 +msgid "Assign Color" +msgstr "Gán màu" + +#: ../mail/em-filter-i18n.h:4 +msgid "Assign Score" +msgstr "Gán điểm" + +#: ../mail/em-filter-i18n.h:5 ../pan/filter-edit-ui.c:796 +msgid "Attachments" +msgstr "Đính kèm" + +#: ../mail/em-filter-i18n.h:6 +msgid "Beep" +msgstr "Bíp" + +#: ../mail/em-filter-i18n.h:7 ui/bookmarks.glade.h:44 +#: ../src/smart-playlist-dialog.c:150 ../pan/filter-edit-ui.c:772 +#: ../pan/filters/filter-phrase.c:238 ../pan/score-add-ui.c:136 +msgid "contains" +msgstr "chứa" + +#: ../mail/em-filter-i18n.h:8 +msgid "Copy to Folder" +msgstr "Chép vào thư mục" + +#: ../mail/em-filter-i18n.h:9 +msgid "Date received" +msgstr "Ngày nhận" + +#: ../mail/em-filter-i18n.h:10 +msgid "Date sent" +msgstr "Ngày gởi" + +#: ../mail/em-filter-i18n.h:13 ../src/smart-playlist-dialog.c:151 +#: ../pan/filter-edit-ui.c:773 ../pan/filters/filter-phrase.c:247 +#: ../pan/score-add-ui.c:137 +msgid "does not contain" +msgstr "không chứa" + +#: ../mail/em-filter-i18n.h:14 ../pan/filter-edit-ui.c:779 +#: ../pan/filters/filter-phrase.c:250 ../pan/score-add-ui.c:143 +msgid "does not end with" +msgstr "không kết thúc bằng" + +#: ../mail/em-filter-i18n.h:15 +msgid "does not exist" +msgstr "không tồn tại" + +#: ../mail/em-filter-i18n.h:16 +msgid "does not return" +msgstr "không trả gởi" + +#: ../mail/em-filter-i18n.h:17 +msgid "does not sound like" +msgstr "không giống với" + +#: ../mail/em-filter-i18n.h:18 ../pan/filter-edit-ui.c:777 +#: ../pan/filters/filter-phrase.c:249 ../pan/score-add-ui.c:141 +msgid "does not start with" +msgstr "không bắt đầu bằng" + +#: ../mail/em-filter-i18n.h:19 +msgid "Do Not Exist" +msgstr "Không tồn tại" + +#: web/template/resources_edit_main.tpl:121 +msgid "Draft" +msgstr "Nháp" + +#: ../mail/em-filter-i18n.h:21 ui/bookmarks.glade.h:46 +#: ../pan/filter-edit-ui.c:778 ../pan/filters/filter-phrase.c:241 +#: ../pan/score-add-ui.c:142 +msgid "ends with" +msgstr "kết thúc bằng" + +#: ../mail/em-filter-i18n.h:22 +msgid "Exist" +msgstr "Tồn tại" + +#: ../mail/em-filter-i18n.h:23 +msgid "exists" +msgstr "tồn tại" + +#: ../mail/em-filter-i18n.h:24 ../gcalctool/calctool.c:73 +msgid "Expression" +msgstr "Biểu thức" + +#: ../mail/em-filter-i18n.h:25 +msgid "Follow Up" +msgstr "Theo dõi tiếp" + +#: ../src/smart-playlist-dialog.c:148 ../pan/filter-edit-ui.c:774 +#: ../pan/filters/filter-phrase.c:239 ../pan/score-add-ui.c:138 +msgid "is" +msgstr "là" + +#: ../mail/em-filter-i18n.h:28 +msgid "is after" +msgstr "có sau" + +#: ../mail/em-filter-i18n.h:29 +msgid "is before" +msgstr "có trước" + +#: ../mail/em-filter-i18n.h:30 +msgid "is Flagged" +msgstr "được đặt cờ" + +#: ../mail/em-filter-i18n.h:31 ../src/smart-playlist-dialog.c:142 +msgid "is greater than" +msgstr "trên" + +#: ../mail/em-filter-i18n.h:32 ../src/smart-playlist-dialog.c:143 +msgid "is less than" +msgstr "dưới" + +#: ../mail/em-filter-i18n.h:33 ../src/smart-playlist-dialog.c:141 +#: ../src/smart-playlist-dialog.c:149 ../pan/filter-edit-ui.c:775 +#: ../pan/filters/filter-phrase.c:248 ../pan/score-add-ui.c:139 +msgid "is not" +msgstr "không là" + +#: ../mail/em-filter-i18n.h:34 +msgid "is not Flagged" +msgstr "không được đặt cờ" + +#: ../mail/em-filter-i18n.h:35 ../mail/mail-config.glade.h:92 +#: ../ui/evolution-mail-message.xml.h:48 +msgid "Junk" +msgstr "Rác" + +#: ../mail/em-filter-i18n.h:36 +msgid "Junk Test" +msgstr "Kiểm tra Rác" + +#: ../mail/em-filter-i18n.h:38 +msgid "Mailing list" +msgstr "Hôp thư chung" + +#: ../mail/em-filter-i18n.h:39 +msgid "Match All" +msgstr "Khớp tất cả" + +#: ../mail/em-filter-i18n.h:40 +msgid "Message Body" +msgstr "Thân thư" + +#: ../mail/em-filter-i18n.h:41 +msgid "Message Header" +msgstr "Dòng đầu thư" + +#: ../mail/em-filter-i18n.h:42 +msgid "Message is Junk" +msgstr "Thư là Rác" + +#: ../mail/em-filter-i18n.h:43 +msgid "Message is not Junk" +msgstr "Thư không phải Rác" + +#: ../mail/em-filter-i18n.h:44 +msgid "Move to Folder" +msgstr "Chuyển vào thư mục" + +#: ../mail/em-filter-i18n.h:45 +msgid "Pipe to Program" +msgstr "Gởi qua ống dẫn đến chương trình" + +#: ../mail/em-filter-i18n.h:46 +msgid "Play Sound" +msgstr "Phát âm thanh" + +#: ../pan/filter-edit-ui.c:811 ../pan/rules/rule-edit-ui.c:151 +#: ../storage/sunone-add-permission-dialog.glade.h:7 +#: ../storage/sunone-permissions-dialog.c:579 +#, fuzzy +msgid "Read" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Đã đọc\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"Đọc" + +#: ../mail/em-filter-i18n.h:48 +msgid "Recipients" +msgstr "Người nhận" + +#: ../mail/em-filter-i18n.h:49 +msgid "Regex Match" +msgstr "Khớp biểu thức chính quy" + +#: ../mail/em-filter-i18n.h:50 +msgid "Replied to" +msgstr "Đã trả lời cho" + +#: ../mail/em-filter-i18n.h:51 +msgid "returns" +msgstr "trả gởi" + +#: ../mail/em-filter-i18n.h:52 +msgid "returns greater than" +msgstr "trả gởi trên" + +#: ../mail/em-filter-i18n.h:53 +msgid "returns less than" +msgstr "trả gởi dưới" + +#: ../mail/em-filter-i18n.h:54 ../src/alleyoop.c:635 ../memprof.glade.h:40 +msgid "Run Program" +msgstr "Chạy chương trình" + +#: ../libgnomeui/gnome-scores.c:94 ../pan/articlelist.c:1058 ../pan/gui.c:1175 +#: ../pan/prefs.c:1392 ../pan/score-add-ui.c:686 ../pan/score-add-ui.c:720 +msgid "Score" +msgstr "Điểm" + +#: ../mail/em-filter-i18n.h:57 +msgid "Set Status" +msgstr "Đặt trạng thái" + +#: ../mail/em-filter-i18n.h:58 +msgid "Size (kB)" +msgstr "Cỡ (kB)" + +#: ../mail/em-filter-i18n.h:59 +msgid "sounds like" +msgstr "giống như" + +#: ../mail/em-filter-i18n.h:60 +msgid "Source Account" +msgstr "Tài khoản nguồn" + +#: ../mail/em-filter-i18n.h:61 +msgid "Specific header" +msgstr "Dòng đầu xác định" + +#: ../mail/em-filter-i18n.h:62 ui/bookmarks.glade.h:52 +#: ../pan/filter-edit-ui.c:776 ../pan/filters/filter-phrase.c:240 +#: ../pan/score-add-ui.c:140 +msgid "starts with" +msgstr "bắt đầu bằng" + +#: ../mail/em-filter-i18n.h:64 +msgid "Stop Processing" +msgstr "Dừng xử lý" + +#: ../mail/em-filter-i18n.h:66 +msgid "Unset Status" +msgstr "Bỏ đặt trạng thái" + +#. and now for the action area +#: ../mail/em-filter-rule.c:488 +msgid "Then" +msgstr "Rồi" + +#: ../mail/em-folder-browser.c:143 ../mail/em-folder-browser.c:134 +msgid "C_reate Search Folder From Search..." +msgstr "Tạo thư mục tìm kiếm từ kết quả tìm kiếm..." + +#. TODO: can this be done in a loop? +#: ../mail/em-folder-properties.c:144 +msgid "Total message:" +msgid_plural "Total message:" +msgstr[0] "Tổng số thư :" + +#: ../mail/em-folder-properties.c:156 +msgid "Unread message:" +msgid_plural "Unread message:" +msgstr[0] "Thư chưa đọc:" + +#: ../mail/em-folder-properties.c:278 +#: ../plugins/groupwise-features/properties.glade.h:3 +msgid "Folder Properties" +msgstr "Thuộc tính thư mục" + +#: ../mail/em-folder-selection-button.c:123 +msgid "" +msgstr "" + +#: ../plug-ins/imagemap/imap_cmd_gimp_guides.c:151 +#: web/template/newaccount_bottom.tpl:2 ../objects/UML/message.c:136 +#: ../glom/glom.glade.h:80 ../glom/mode_design/users/dialog_groups_list.cc:76 +msgid "Create" +msgstr "Tạo" + +#: ../mail/em-folder-selector.c:265 +#: ../shell/e-folder-creation-dialog.glade.h:2 +msgid "Folder _name:" +msgstr "_Tên thư mục:" + +#: ../mail/mail-vfolder.c:937 ../mail/mail-vfolder.c:1007 +msgid "Search Folders" +msgstr "Thư mục tìm kiếm" + +#: ../mail/em-folder-tree-model.c:207 ../mail/em-folder-tree-model.c:209 +msgid "UNMATCHED" +msgstr "KHÔNG KHỚP" + +#: ../storage/exchange-hierarchy-foreign.c:253 ../src/journal.c:821 +#: ../src/journal.c:1091 +msgid "Drafts" +msgstr "Nháp" + +#: ../src/history.c:111 ../src/history.c:154 +msgid "Sent" +msgstr "Đã gởi" + +#: ../mail/em-folder-tree.c:694 ../mail/em-folder-tree.c:689 +msgid "Mail Folder Tree" +msgstr "Cây thư mục thư" + +#: ../mail/em-folder-tree.c:854 +#, c-format +msgid "Moving folder %s" +msgstr "Đang chuyển thư mục « %s »" + +#: ../mail/em-folder-tree.c:856 ../mail/em-folder-tree.c:851 +#, c-format +msgid "Copying folder %s" +msgstr "Đang sao chép thư mục « %s »" + +#: ../mail/em-folder-tree.c:858 ../mail/message-list.c:1613 +#, c-format +msgid "Moving messages into folder %s" +msgstr "Đang chuyển thư vào thư mục « %s »" + +#: ../mail/em-folder-tree.c:865 ../mail/message-list.c:1623 +#: ../mail/em-folder-tree.c:860 ../mail/message-list.c:1615 +#, c-format +msgid "Copying messages into folder %s" +msgstr "Đang sao chép thư vào thư mục « %s »" + +#: ../mail/em-folder-tree.c:881 ../mail/em-folder-tree.c:876 +msgid "Cannot drop message(s) into toplevel store" +msgstr "Không thả được thư vào kho mức độ đỉnh" + +#: ../mail/em-folder-tree.c:977 ../ui/evolution-mail-message.xml.h:105 +#: ../mail/em-folder-tree.c:972 ../ui/evolution-mail-message.xml.h:101 +msgid "_Copy to Folder" +msgstr "_Chép vào thư mục" + +#: ../mail/em-folder-tree.c:978 ../ui/evolution-mail-message.xml.h:116 +#: ../mail/em-folder-tree.c:973 ../ui/evolution-mail-message.xml.h:111 +msgid "_Move to Folder" +msgstr "_Chuyển vào thư mục" + +#: ../mail/em-folder-tree.c:1683 ../mail/mail-ops.c:1058 +#: ../mail/em-folder-tree.c:1678 ../mail/mail-ops.c:1057 +#, c-format +msgid "Scanning folders in \"%s\"" +msgstr "Đang quét các thư mục trong « %s »" + +#: ../Pyblio/GnomeUI/Document.py:146 po/silky.glade.h:221 +msgid "_View" +msgstr "_Xem" + +#: ../mail/em-folder-tree.c:2043 ../mail/em-folder-tree.c:2032 +msgid "Open in _New Window" +msgstr "Mở trong cửa sổ mớ_i" + +#: ../mail/em-folder-tree.c:2047 ../mail/em-folder-tree.c:2036 +msgid "_Copy..." +msgstr "_Chép..." + +#: ../mail/em-folder-tree.c:2048 ../mail/em-folder-tree.c:2037 +msgid "_Move..." +msgstr "_Di chuyển..." + +#: ../storage/xc-commands.c:391 +msgid "_New Folder..." +msgstr "Thư mục _mới..." + +#: ../mail/em-folder-tree.c:2055 ../ui/evolution-mail-list.xml.h:30 +msgid "_Rename..." +msgstr "Đổi _tên..." + +#: ../libnautilus-private/nautilus-file-operations.c:2639 +msgid "_Empty Trash" +msgstr "Đổ _Rác" + +#: ../mail/em-folder-utils.c:104 +#, c-format +msgid "Copying `%s' to `%s'" +msgstr "Đang sao chép « %s » vào « %s »..." + +#: ../mail/importers/evolution-outlook-importer.c:134 +msgid "Select folder" +msgstr "Chọn thư mục" + +#: ../mail/em-folder-utils.c:368 ../mail/em-folder-view.c:982 +#: ../mail/em-folder-view.c:946 +msgid "C_opy" +msgstr "_Chép" + +#: ../mail/em-folder-utils.c:503 ../shell/e-folder-misc-dialogs.c:188 +#: ../storage/sunone-folder-tree.c:1058 +#, c-format +msgid "Rename the \"%s\" folder to:" +msgstr "Đổi tên thư mục « %s » thành:" + +#: ../mail/em-folder-utils.c:505 ../shell/e-folder-misc-dialogs.c:191 +msgid "Rename Folder" +msgstr "Đổi tên thư mục" + +#: ../mail/em-folder-utils.c:511 +msgid "Folder names cannot contain '/'" +msgstr "Tên thư mục không thể chứa ký tự sổ chéo." + +#: ../plugins/groupwise-features/share-folder-common.c:140 +#, c-format +msgid "Creating folder `%s'" +msgstr "Đang tạo thư mục « %s »" + +#: ../plugins/groupwise-features/share-folder-common.c:384 +msgid "Create folder" +msgstr "Tạo thư mục" + +#: ../shell/e-folder-creation-dialog.glade.h:4 +msgid "Specify where to create the folder:" +msgstr "Xác định nơi tạo thư mục đó :" + +#: ../mail/em-folder-view.c:1075 ../ui/evolution-mail-message.xml.h:125 +#: ../mail/em-folder-view.c:1040 ../ui/evolution-mail-message.xml.h:119 +msgid "_Reply to Sender" +msgstr "T_rả lời người gởi" + +#: ../mail/em-popup.c:546 ../mail/em-popup.c:557 +msgid "Reply to _All" +msgstr "Trả lời _mọi người" + +#: ui/galeon-ui.xml.in.h:177 +msgid "_Forward" +msgstr "_Chuyển tiếp" + +#: ../mail/em-folder-view.c:1080 ../ui/evolution-mail-message.xml.h:107 +#: ../mail/em-folder-view.c:1044 ../ui/evolution-mail-message.xml.h:103 +msgid "_Edit as New Message..." +msgstr "_Hiệu chỉnh dạng thư mới..." + +#: ../mail/em-folder-view.c:1086 ../mail/em-folder-view.c:1050 +msgid "U_ndelete" +msgstr "_Hủy xoá bỏ" + +#: ../mail/em-folder-view.c:1087 ../ui/evolution-addressbook.xml.h:37 +#: ../mail/em-folder-view.c:1051 ../ui/evolution-addressbook.xml.h:36 +msgid "_Move to Folder..." +msgstr "_Chuyển sang thư mục..." + +#: ../mail/em-folder-view.c:1088 ../ui/evolution-addressbook.xml.h:33 +#: ../mail/em-folder-view.c:1052 ../ui/evolution-addressbook.xml.h:32 +msgid "_Copy to Folder..." +msgstr "_Chép vào thư mục..." + +#: ../mail/em-folder-view.c:1091 ../mail/em-folder-view.c:1055 +msgid "Mar_k as Read" +msgstr "Đánh dấu Đã đọ_c" + +#: ../mail/em-folder-view.c:1092 ../mail/em-folder-view.c:1056 +msgid "Mark as _Unread" +msgstr "Đánh dấu C_hưa đọc" + +#: ../mail/em-folder-view.c:1093 ../mail/em-folder-view.c:1057 +msgid "Mark as _Important" +msgstr "Đánh dấu _Quan trọng" + +#: ../mail/em-folder-view.c:1094 ../mail/em-folder-view.c:1058 +msgid "_Mark as Unimportant" +msgstr "Đánh dấu _Không quan trọng" + +#: ../mail/em-folder-view.c:1095 ../mail/em-folder-view.c:1059 +msgid "Mark as _Junk" +msgstr "Đánh dấu _Rác" + +#: ../mail/em-folder-view.c:1096 ../mail/em-folder-view.c:1060 +msgid "Mark as _Not Junk" +msgstr "Đánh dấu Không Rá_c" + +#: ../mail/em-folder-view.c:1097 ../mail/em-folder-view.c:1061 +msgid "Mark for Follo_w Up..." +msgstr "Đánh dấu Cần theo _dõi tiếp..." + +#: ../mail/em-folder-view.c:1105 ../mail/em-folder-view.c:1069 +msgid "Fla_g Completed" +msgstr "Cờ _hoàn tất" + +#: ../mail/em-folder-view.c:1106 ../mail/em-folder-view.c:1070 +msgid "Cl_ear Flag" +msgstr "Xó_a cờ" + +#: ../mail/em-folder-view.c:1109 ../mail/em-folder-view.c:1073 +msgid "Crea_te Rule From Message" +msgstr "Tạo _quy tắc từ thư" + +#: ../mail/em-folder-view.c:1110 ../mail/em-folder-view.c:1074 +msgid "Search Folder from _Subject" +msgstr "Thư mục tìm kiếm trên _Chủ đề" + +#: ../mail/em-folder-view.c:1111 ../mail/em-folder-view.c:1075 +msgid "Search Folder from Se_nder" +msgstr "Thư mục tìm kiếm trên _Người gởi" + +#: ../mail/em-folder-view.c:1112 ../mail/em-folder-view.c:1076 +msgid "Search Folder from _Recipients" +msgstr "Thư mục tìm kiếm trên N_gười nhận" + +#: ../mail/em-folder-view.c:1113 ../mail/em-folder-view.c:1077 +msgid "Search Folder from Mailing _List" +msgstr "Thư mục tìm kiếm trên _Hộp thư chung" + +#: ../mail/em-folder-view.c:1117 ../mail/em-folder-view.c:1081 +msgid "Filter on Sub_ject" +msgstr "Lọc theo _Chủ đề" + +#: ../mail/em-folder-view.c:1118 ../mail/em-folder-view.c:1082 +msgid "Filter on Sen_der" +msgstr "Lọc theo _Người gởi" + +#: ../mail/em-folder-view.c:1119 ../mail/em-folder-view.c:1083 +msgid "Filter on Re_cipients" +msgstr "Lọc theo N_gười nhận" + +#: ../mail/em-folder-view.c:1120 ../mail/em-folder-view.c:1084 +msgid "Filter on _Mailing List" +msgstr "Lọc theo _Hộp thư chung" + +#: ../plugins/print-message/print-message.c:83 +msgid "Print Message" +msgstr "In thư" + +#: ../mail/em-folder-view.c:2257 ../mail/em-folder-view.c:2220 +msgid "Unable to retrieve message" +msgstr "Không nhận được thư." + +#: ../mail/em-folder-view.c:2450 ../mail/em-folder-view.c:2413 +msgid "_Copy Link Location" +msgstr "Sao chép địa chỉ _liên kết" + +#: ../mail/em-folder-view.c:2452 ../mail/em-folder-view.c:2415 +msgid "Create _Search Folder" +msgstr "Tạo Thư mục tìm _kiếm" + +#: ../mail/em-folder-view.c:2453 ../mail/em-folder-view.c:2416 +msgid "_From this Address" +msgstr "_Từ địa chỉ này" + +#: ../mail/em-folder-view.c:2454 ../mail/em-folder-view.c:2417 +msgid "_To this Address" +msgstr "_Cho địa chỉ này" + +#: ../mail/em-folder-view.c:2790 ../mail/em-folder-view.c:2698 +#, c-format +msgid "Click to mail %s" +msgstr "Nhấn để gởi thư cho « %s »" + +#. message-search popup match count string +#: ../mail/em-format-html-display.c:472 ../mail/em-format-html-display.c:442 +#, c-format +msgid "Matches: %d" +msgstr "Khớp: %d" + +#: ../mail/em-format-html-display.c:729 ../mail/em-format-html.c:607 +#: ../mail/em-format-html-display.c:694 ../mail/em-format-html.c:596 +#: app/sample-editor.c:1541 +msgid "Unsigned" +msgstr "Chưa ký" + +#: ../mail/em-format-html-display.c:729 ../mail/em-format-html-display.c:694 +msgid "" +"This message is not signed. There is no guarantee that this message is " +"authentic." +msgstr "Thư này không có chữ ký nên không thể đảm bảo thư này do người đó gởi." + +#: ../mail/em-format-html-display.c:730 ../mail/em-format-html.c:608 +#: ../mail/em-format-html-display.c:695 ../mail/em-format-html.c:597 +msgid "Valid signature" +msgstr "Chữ ký hợp lệ" + +#: ../mail/em-format-html-display.c:730 ../mail/em-format-html-display.c:695 +msgid "" +"This message is signed and is valid meaning that it is very likely that this " +"message is authentic." +msgstr "Thư này có chữ ký và hợp lệ nên rất có thể là thư đó do người đó gởi." + +#: ../mail/em-format-html-display.c:731 ../mail/em-format-html.c:609 +#: ../mail/em-format-html-display.c:696 ../mail/em-format-html.c:598 +msgid "Invalid signature" +msgstr "Chữ ký không hợp lệ" + +#: ../mail/em-format-html-display.c:731 ../mail/em-format-html-display.c:696 +msgid "" +"The signature of this message cannot be verified, it may have been altered " +"in transit." +msgstr "" +"Không thể xác minh chữ ký của thư này: có lẽ nó bị giả trong khi truyền." + +#: ../mail/em-format-html-display.c:732 ../mail/em-format-html-display.c:697 +msgid "Valid signature, cannot verify sender" +msgstr "Chữ ký hợp lệ nhưng mà không thể xác minh người gởi." + +#: ../mail/em-format-html-display.c:732 ../mail/em-format-html-display.c:697 +msgid "" +"This message is signed with a valid signature, but the sender of the message " +"cannot be verified." +msgstr "Thư này có chữ ký hợp lệ, nhưng mà không thể xác minh người gởi thư." + +#: ../mail/em-format-html-display.c:738 ../mail/em-format-html.c:616 +#: ../mail/em-format-html-display.c:703 ../mail/em-format-html.c:605 +msgid "Unencrypted" +msgstr "Không mật mã" + +#: ../mail/em-format-html-display.c:738 ../mail/em-format-html-display.c:703 +msgid "" +"This message is not encrypted. Its content may be viewed in transit across " +"the Internet." +msgstr "" +"Thư này không mật mã nên bất cứ người nào có xem được nội dung nó trong khi " +"truyền qua Mạng." + +#: ../mail/em-format-html-display.c:739 ../mail/em-format-html.c:617 +#: ../mail/em-format-html-display.c:704 ../mail/em-format-html.c:606 +msgid "Encrypted, weak" +msgstr "Mật mã yếu" + +#: ../mail/em-format-html-display.c:739 ../mail/em-format-html-display.c:704 +msgid "" +"This message is encrypted, but with a weak encryption algorithm. It would " +"be difficult, but not impossible for an outsider to view the content of this " +"message in a practical amount of time." +msgstr "" +"Thư này mật mã, nhưng mà dùng thuật toán mật mã yếu. Người khác sẽ gặp khó " +"khăn đọc thư này được một thời gian hữu ích, nhưng mà có thể làm như thế." + +#: ../xpdf/pdf-info-dict-util.cc:203 +msgid "Encrypted" +msgstr "Đã mật mã" + +#: ../mail/em-format-html-display.c:740 ../mail/em-format-html-display.c:705 +msgid "" +"This message is encrypted. It would be difficult for an outsider to view " +"the content of this message." +msgstr "Thư này mật mã. Người khác sẽ gặp khó khăn nhiều đọc nội dung thư." + +#: ../mail/em-format-html-display.c:741 ../mail/em-format-html.c:619 +#: ../mail/em-format-html-display.c:706 ../mail/em-format-html.c:608 +msgid "Encrypted, strong" +msgstr "Đã mật mã mạnh" + +#: ../mail/em-format-html-display.c:741 ../mail/em-format-html-display.c:706 +msgid "" +"This message is encrypted, with a strong encryption algorithm. It would be " +"very difficult for an outsider to view the content of this message in a " +"practical amount of time." +msgstr "" +"Thư này mật mã dùng thuật toán mật mã mạnh. Người khác sẽ gặp khó khăn rất " +"nhiều đọc nội dung thư được thời gian hữu ích. " + +#: mozilla/GtkNSSDialogs.cpp:175 mozilla/GtkNSSDialogs.cpp:459 +msgid "_View Certificate" +msgstr "_Xem Chứng nhận" + +#: ../mail/em-format-html-display.c:857 ../mail/em-format-html-display.c:822 +msgid "This certificate is not viewable" +msgstr "Chứng nhận này không thể xem" + +#: ../mail/em-format-html-display.c:1145 ../mail/em-format-html-display.c:1104 +msgid "Completed on %B %d, %Y, %l:%M %p" +msgstr "Hoàn thành lúc %d %B, %Y, %l:%M %p" + +#: ../mail/em-format-html-display.c:1153 ../mail/em-format-html-display.c:1112 +msgid "Overdue:" +msgstr "Quá hạn:" + +#: ../mail/em-format-html-display.c:1156 ../mail/em-format-html-display.c:1115 +msgid "by %B %d, %Y, %l:%M %p" +msgstr "trước %d %B, %Y, %l:%M %p" + +#: ../mail/em-format-html-display.c:1216 ../mail/em-format-html-display.c:1175 +msgid "_View Inline" +msgstr "_Xem trực tiếp" + +#: ../mail/em-format-html-display.c:1218 ../mail/em-format-html-display.c:1177 +msgid "_Fit to Width" +msgstr "_Vừa khít độ rộng" + +#: ../mail/em-format-html-display.c:1219 ../mail/em-format-html-display.c:1178 +msgid "Show _Original Size" +msgstr "Hiển thị kích thước _gốc" + +#: ../mail/em-format-html-display.c:1596 ../mail/em-format-html-display.c:1546 +msgid "Attachment Button" +msgstr "Nút đính kèm" + +#: ../mail/em-format-html-display.c:1803 ../mail/em-format-html-display.c:1753 +msgid "Select folder to save all attachments..." +msgstr "Chọn thư mục nơi cần lưu mọi đính kèm..." + +#: ../mail/em-format-html-display.c:1850 ../mail/em-format-html-display.c:1799 +msgid "Save Selected..." +msgstr "Lưu các điều chọn..." + +#. Cant i put in the number of attachments here ? +#: ../mail/em-format-html-display.c:1917 ../mail/em-format-html-display.c:1866 +#, c-format +msgid "%d attachment" +msgid_plural "%d attachment" +msgstr[0] "%d đính kèm" + +#: ../mail/em-format-html-display.c:1980 +msgid "Toggle Attachment Bar" +msgstr "Bật/tắt thanh đính kèm" + +#: ../mail/em-format-html-display.c:1982 ../mail/em-format-html-display.c:1920 +msgid "No Attachment" +msgstr "Không có đính kèm" + +#: ../mail/em-format-html-display.c:1985 ../mail/em-format-html-display.c:1923 +msgid "Save All" +msgstr "Lưu tất cả" + +#: ../mail/em-format-html-print.c:130 +#, c-format +msgid "Page %d of %d" +msgstr "Trang %d trên %d" + +#: ../mail/em-format-html.c:483 ../mail/em-format-html.c:485 +#: ../mail/em-format-html.c:474 ../mail/em-format-html.c:476 +#, c-format +msgid "Retrieving `%s'" +msgstr "Đang nhận « %s »" + +#: ../mail/em-format-html.c:610 ../mail/em-format-html.c:599 +msgid "Valid signature but cannot verify sender" +msgstr "Chữ ký hợp lệ nhưng mà không thể xác minh người gởi." + +#: ../mail/em-format-html.c:880 ../mail/em-format-html.c:967 +msgid "Malformed external-body part." +msgstr "Phần thân ở ngoại dạng sai." + +#: ../mail/em-format-html.c:910 ../mail/em-format-html.c:997 +#, c-format +msgid "Pointer to FTP site (%s)" +msgstr "Trỏ tới địa chỉ FTP (%s)" + +#: ../mail/em-format-html.c:921 +#, c-format +msgid "Pointer to local file (%s) valid at site \"%s\"" +msgstr "Trỏ tới tập tin cục bộ (%s) hợp lệ tại nơi Mạng « %s »" + +#: ../mail/em-format-html.c:923 ../mail/em-format-html.c:1010 +#, c-format +msgid "Pointer to local file (%s)" +msgstr "Trỏ tới tập tin cục bộ (%s)" + +#: ../mail/em-format-html.c:944 ../mail/em-format-html.c:1031 +#, c-format +msgid "Pointer to remote data (%s)" +msgstr "Trỏ tới dữ liệu ở xa (%s)" + +#: ../mail/em-format-html.c:955 ../mail/em-format-html.c:1042 +#, c-format +msgid "Pointer to unknown external data (\"%s\" type)" +msgstr "Trỏ tới dữ liệu lạ bên ngoài (kiểu « %s »)" + +#: ../mail/em-format-html.c:1181 ../mail/em-format-html.c:1270 +msgid "Formatting message" +msgstr "Đang định dạng thư" + +#: ../pan/prefs.c:1635 ../pan/text.c:713 +msgid "Reply-To" +msgstr "Trả lời" + +#: ../mail/em-mailer-prefs.c:86 +msgid "Cc" +msgstr "Cc" + +#: ../mail/em-mailer-prefs.c:87 +msgid "Bcc" +msgstr "Bcc" + +#: ../mail/em-mailer-prefs.c:987 ../addressbook/libebook/e-contact.c:152 +#: ../mimedir/mimedir-vcard.c:397 +msgid "Mailer" +msgstr "Trình thư" + +#. translators: strftime format for local time equivalent in Date header display, with day +#: ../mail/em-format-html.c:1613 ../mail/em-format-html.c:1702 +msgid " (%a, %R %Z)" +msgstr " (%a, %R %Z)" + +#. translators: strftime format for local time equivalent in Date header display, without day +#: ../mail/em-format-html.c:1616 ../mail/em-format-html.c:1705 +msgid " (%R %Z)" +msgstr " (%R %Z)" + +#: ../pan/rules/rule-edit-ui.c:757 ../pan/score-add-ui.c:595 ../pan/text.c:717 +msgid "Newsgroups" +msgstr "Nhóm tin" + +#: ../mail/em-format.c:1102 ../mail/em-format.c:1101 +#, c-format +msgid "%s attachment" +msgstr "%s đính kèm" + +#: ../mail/em-format.c:1141 ../mail/em-format.c:1288 ../mail/em-format.c:1575 +#: ../mail/em-format.c:1140 ../mail/em-format.c:1279 +msgid "Could not parse S/MIME message: Unknown error" +msgstr "Không thể phân tách thư S/MIME. Không biết sao." + +#: ../mail/em-format.c:1270 ../mail/em-format.c:1426 ../mail/em-format.c:1417 +msgid "Could not parse MIME message. Displaying as source." +msgstr "Không thể phân tách thư MIME nên hiện thị mã nguồn." + +#: ../mail/em-format.c:1278 ../mail/em-format.c:1269 +msgid "Unsupported encryption type for multipart/encrypted" +msgstr "Kiểu mật mã không được hỗ trợ cho: đa phần/mật mã" + +#: ../mail/em-format.c:1445 ../mail/em-format.c:1436 +msgid "Unsupported signature format" +msgstr "Dạng thức chữ ký không hỗ trợ" + +#: ../mail/em-format.c:1453 ../mail/em-format.c:1517 ../mail/em-format.c:1444 +msgid "Error verifying signature" +msgstr "Gặp lỗi khi xác minh chữ ký" + +#: ../mail/em-format.c:1453 ../mail/em-format.c:1517 ../mail/em-format.c:1444 +msgid "Unknown error verifying signature" +msgstr "Gặp lỗi lạ khi xác minh chữ ký." + +#: ../mail/em-mailer-prefs.c:103 ../mail/em-mailer-prefs.c:101 +msgid "Every time" +msgstr "Mọi lần" + +#: ../mail/em-mailer-prefs.c:104 ../mail/em-mailer-prefs.c:102 +msgid "Once per day" +msgstr "Một lần mỗi ngày" + +#: ../mail/em-mailer-prefs.c:105 ../mail/em-mailer-prefs.c:103 +msgid "Once per week" +msgstr "Một lần mỗi tuần" + +#: ../mail/em-mailer-prefs.c:106 ../mail/em-mailer-prefs.c:104 +msgid "Once per month" +msgstr "Một lần mỗi tháng" + +#: ../mail/em-migrate.c:1208 ../mail/em-migrate.c:1198 +msgid "" +"The location and hierarchy of the Evolution mailbox folders has changed " +"since Evolution 1.x.\n" +"\n" +"Please be patient while Evolution migrates your folders..." +msgstr "" +"Vị trí và cây của các thư mục hộp thư Evolution đã thay đổi so sánh với " +"trình Evolution phiên bản 1.x.\n" +"\n" +"Hãy kiên nhẫn trong khi Evolution chuyển đổi các thư mục của bạn..." + +#: ../mail/em-migrate.c:1647 ../mail/em-migrate.c:1632 +#, c-format +msgid "Unable to create new folder `%s': %s" +msgstr "Không thể tạo thư mục mới « %s »: %s" + +#: ../mail/em-migrate.c:1673 ../mail/em-migrate.c:1658 +#, c-format +msgid "Unable to copy folder `%s' to `%s': %s" +msgstr "Không sao chép được thư mục « %s » thành « %s »: %s" + +#: ../mail/em-migrate.c:1858 ../mail/em-migrate.c:1843 +#, c-format +msgid "Unable to scan for existing mailboxes at `%s': %s" +msgstr "Không quét được tìm hộp thư đã có tại « %s »: %s" + +#: ../mail/em-migrate.c:2062 ../mail/em-migrate.c:2047 +#, c-format +msgid "Unable to open old POP keep-on-server data `%s': %s" +msgstr "Không thể mở dữ liệu giữ-trên-máy-chủ POP cũ « %s »: %s" + +#: ../mail/em-migrate.c:2076 +#, c-format +msgid "Unable to create POP3 keep-on-server data directory `%s': %s" +msgstr "Không thể tạo thư mục dữ liệu giữ-trên-máy-chủ POP3 « %s »: %s" + +#: ../mail/em-migrate.c:2105 ../mail/em-migrate.c:2090 +#, c-format +msgid "Unable to copy POP3 keep-on-server data `%s': %s" +msgstr "Không sao chép được dữ liệu giữ-trên-máy-chủ POP3 « %s »: %s" + +#: ../mail/em-migrate.c:2576 ../mail/em-migrate.c:2588 +#: ../mail/em-migrate.c:2561 ../mail/em-migrate.c:2573 +#, c-format +msgid "Failed to create local mail storage `%s': %s" +msgstr "Không tạo kho thư địa phương được « %s »: %s" + +#: ../mail/em-migrate.c:2711 ../mail/em-migrate.c:2693 +#, c-format +msgid "Unable to create local mail folders at `%s': %s" +msgstr "Không thể tạo những thư mục thư địa phương tại « %s »: %s" + +#: ../mail/em-migrate.c:2730 ../mail/em-migrate.c:2711 +msgid "" +"Unable to read settings from previous Evolution install, `evolution/config." +"xmldb' does not exist or is corrupt." +msgstr "" +"Không thể đọc thiết lập từ bản cài đặt Evolution cũ : tập tin bị hỏng hay không tồn tại." + +#: ../mail/em-popup.c:556 ../mail/em-popup.c:567 ../mail/em-popup.c:544 +#: ../mail/em-popup.c:555 +msgid "_Reply to sender" +msgstr "T_rả lời người gởi" + +#: ../mail/em-popup.c:556 ../ui/evolution-mail-message.xml.h:81 +msgid "Reply to _List" +msgstr "Trả lời _Hộp thư chung" + +#: ../mail/em-popup.c:619 ../mail/em-popup.c:607 +msgid "_Open Link in Browser" +msgstr "_Mở liên kết bằng trình duyệt" + +#: ../mail/em-popup.c:620 ../mail/em-popup.c:608 +msgid "_Send New Message To..." +msgstr "_Gởi thư mới cho..." + +#: ../mail/em-popup.c:621 ../mail/em-popup.c:609 +msgid "_Add to Addressbook" +msgstr "_Thêm vào Sổ địa chỉ" + +#: ../mail/em-subscribe-editor.c:615 ../mail/em-subscribe-editor.c:614 +msgid "This store does not support subscriptions, or they are not enabled." +msgstr "Kho này không hỗ trợ đăng ký, hay chưa hiệu lực khả năng đó." + +#: ../mail/em-subscribe-editor.c:644 ../mail/em-subscribe-editor.c:643 +#: ../src/red_subscriptions.py:57 ../pan/grouplist.c:382 +#: ../pan/grouplist.c:957 +msgid "Subscribed" +msgstr "Đã đăng ký" + +#: ../mail/em-subscribe-editor.c:648 ../libgimpwidgets/gimppatheditor.c:252 +#: ../app/core/gimpimagefile.c:575 ../app/dialogs/preferences-dialog.c:1593 +#: src/gtkam-tree.c:1318 ../sheets/Misc.sheet.in.h:2 +msgid "Folder" +msgstr "Thư mục" + +#. FIXME: This is just to get the shadow, is there a better way? +#: ../mail/em-subscribe-editor.c:869 ../mail/em-subscribe-editor.c:862 +msgid "Please select a server." +msgstr "Hãy chọn máy phục vụ." + +#: ../mail/em-subscribe-editor.c:890 ../mail/em-subscribe-editor.c:883 +msgid "No server has been selected" +msgstr "Chưa chọn máy phục vụ." + +#: ../mail/em-utils.c:122 ../mail/em-utils.c:105 +msgid "Don't show this message again." +msgstr "Đừng hiện thông điệp này lần nữa." + +#: ../pan/filter-ui.c:364 ../pan/gui.c:1156 ../pan/rules/rule-edit-ui.c:760 +msgid "Filters" +msgstr "Lọc" + +#: ../mail/em-utils.c:479 +msgid "message" +msgstr "thư" + +#: ../mail/em-utils.c:670 ../mail/em-utils.c:614 +msgid "Save Message..." +msgstr "Lưu thư..." + +#: ../mail/em-utils.c:719 ../mail/em-utils.c:663 +msgid "Add address" +msgstr "Thêm địa chỉ" + +#: ../mail/em-utils.c:1198 ../mail/em-utils.c:1142 +#, c-format +msgid "Messages from %s" +msgstr "Thừ từ « %s »" + +#: ../mail/em-vfolder-editor.c:112 ../mail/em-vfolder-editor.c:104 +msgid "Search _Folders" +msgstr "_Thư mục tìm kiếm" + +#: ../mail/em-vfolder-rule.c:576 ../mail/em-vfolder-rule.c:574 +msgid "Search Folder source" +msgstr "Nguồn thư mục tìm kiếm" + +#: ../mail/evolution-mail.schemas.in.in.h:1 +msgid "Automatic link recognition" +msgstr "Tự động nhận dạng liên kết" + +#: ../mail/evolution-mail.schemas.in.in.h:2 +msgid "Automatic smiley recognition" +msgstr "Tự động nhận dạng biểu tượng xúc cảm" + +#: ../mail/evolution-mail.schemas.in.in.h:3 +msgid "Check incoming mail being junk" +msgstr "Kiểm thư mới nhận là thư rác" + +#: ../mail/evolution-mail.schemas.in.in.h:4 +msgid "Citation highlight color" +msgstr "Màu tô sáng trích dẫn" + +#: ../mail/evolution-mail.schemas.in.in.h:5 +msgid "Citation highlight color." +msgstr "Màu tô sáng trích dẫn." + +#: ../mail/evolution-mail.schemas.in.in.h:6 +msgid "Composer Window default height" +msgstr "Độ cao mặc định cửa sổ soạn" + +#: ../mail/evolution-mail.schemas.in.in.h:7 +msgid "Composer Window default width" +msgstr "Độ rộng mặc định cửa sổ soạn" + +#: ../mail/evolution-mail.schemas.in.in.h:8 +msgid "Default charset in which to compose messages" +msgstr "Bộ ký tự mặc định để soạn thảo thư." + +#: ../mail/evolution-mail.schemas.in.in.h:9 +msgid "Default charset in which to compose messages." +msgstr "Bộ ký tự mặc định để soạn thảo thư." + +#: ../mail/evolution-mail.schemas.in.in.h:10 +msgid "Default charset in which to display messages" +msgstr "Bộ ký tự mặc định để hiển thị thư." + +#: ../mail/evolution-mail.schemas.in.in.h:11 +msgid "Default charset in which to display messages." +msgstr "Bộ ký tự mặc định để hiển thị thư." + +#: ../mail/evolution-mail.schemas.in.in.h:12 +msgid "Default forward style" +msgstr "Kiểu chuyển tiếp mặc định" + +#: ../mail/evolution-mail.schemas.in.in.h:13 +msgid "Default height of the Composer Window" +msgstr "Độ cao mặc định cửa Cửa sổ Soạn" + +#: ../mail/evolution-mail.schemas.in.in.h:14 +msgid "Default height of the Message Window" +msgstr "Độ cao mặc định cửa Cửa sổ Thư" + +#: ../mail/evolution-mail.schemas.in.in.h:15 +msgid "Default height of the Subscribe dialog" +msgstr "Độ cao mặc định cửa hộp thoại Đăng ký" + +#: ../mail/evolution-mail.schemas.in.in.h:16 +msgid "Default reply style" +msgstr "Kiểu trả lời mặc định" + +#: ../mail/evolution-mail.schemas.in.in.h:17 +msgid "Default width of the Composer Window" +msgstr "Độ rộng mặc định cửa Cửa sổ Soạn" + +#: ../mail/evolution-mail.schemas.in.in.h:18 +msgid "Default width of the Message Window" +msgstr "Độ rộng mặc định cửa Cửa sổ Thư" + +#: ../mail/evolution-mail.schemas.in.in.h:19 +msgid "Default width of the Subscribe dialog" +msgstr "Độ rộng mặc định cửa hộp thoại Đăng ký" + +#: ../mail/evolution-mail.schemas.in.in.h:20 +msgid "Draw spelling error indicators on words as you type." +msgstr "Vẽ chỉ báo lỗi chính tả trên từ khi gõ." + +#: ../mail/evolution-mail.schemas.in.in.h:21 +msgid "Empty Trash folders on exit" +msgstr "Đổ các thư mục Sọt rác khi thoát" + +#: ../mail/evolution-mail.schemas.in.in.h:22 +msgid "Empty all Trash folders when exiting Evolution." +msgstr "Đổ các thư mục Sọt rác khi thoát trình Evolution." + +#: ../mail/evolution-mail.schemas.in.in.h:23 +msgid "Enable caret mode, so that you can see a cursor when reading mail." +msgstr "Hiệu lực chế độ con nháy, để bạn xem con chạy khi đọc thư." + +#: ../mail/evolution-mail.schemas.in.in.h:24 +msgid "Enable/disable caret mode" +msgstr "Bật/tắt chế độ con nháy" + +#: ../mail/evolution-mail.schemas.in.in.h:25 +msgid "Height of the message-list pane" +msgstr "Độ cao ô cửa sổ danh sách thư" + +#: ../mail/evolution-mail.schemas.in.in.h:26 +msgid "Height of the message-list pane." +msgstr "Độ cao ô cửa sổ danh sách thư." + +#: ../mail/evolution-mail.schemas.in.in.h:27 +msgid "" +"If a user tries to open 10 or more messages at one time, ask the user if " +"they really want to do it." +msgstr "" +"Nếu người dùng cố mở hơn 9 thư cùng lúc thì hỏi nếu họ thật muốn làm như thế." + +#: ../mail/evolution-mail.schemas.in.in.h:28 +msgid "" +"If there isn't a builtin viewer for a particular mime-type inside Evolution, " +"any mime-types appearing in this list which map to a bonobo-component viewer " +"in GNOME's mime-type database may be used for displaying content." +msgstr "" +"Nếu Evolution không có bộ xem có sẵn cho một kiểu MIME nào đó thì sẽ dùng " +"bất cứ bộ xem tương thích với Bonobo nào sẵn sàng trong cơ sở dữ liệu kiểu " +"MIME của GNOME." + +#: ../mail/evolution-mail.schemas.in.in.h:29 +msgid "Last time empty trash was run" +msgstr "Lần cuối cùng đã đổ Sọt Rác" + +#: ../mail/evolution-mail.schemas.in.in.h:30 +msgid "List of Labels and their associated colors" +msgstr "Danh sách Nhãn và màu sắc liên quan" + +#: ../mail/evolution-mail.schemas.in.in.h:31 +msgid "List of accepted licenses" +msgstr "Danh sách các quyền đã chấp nhận" + +#: ../mail/evolution-mail.schemas.in.in.h:32 +msgid "List of accounts" +msgstr "Danh sách các tài khoản" + +#: ../mail/evolution-mail.schemas.in.in.h:33 +msgid "" +"List of accounts known to the mail component of Evolution. The list contains " +"strings naming subdirectories relative to /apps/evolution/mail/accounts." +msgstr "" +"Danh sách các tài khoản mà thành phần thư của Evolution biết được. Danh sách " +"ấy chứa chuỗi lập tên của các thư mục con liên quan với /apps/evolution/mail/" +"accounts." + +#: ../mail/evolution-mail.schemas.in.in.h:34 +msgid "List of custom headers and whether they are enabled." +msgstr "Danh sách các dòng đầu tự chọn và nếu bật chưa." + +#: ../mail/evolution-mail.schemas.in.in.h:35 +msgid "" +"List of labels known to the mail component of Evolution. The list contains " +"strings containing name:color where color uses the HTML hex encoding." +msgstr "" +"Danh sách các nhãn mà thành phần thư của Evolution biết được. Danh sách đó " +"chứa chuỗi name:color (tên:màu) mà màu đó dạng hệ thập lục phân HTML." + +#: ../mail/evolution-mail.schemas.in.in.h:36 +msgid "List of mime types to check for bonobo component viewers" +msgstr "" +"Danh sách các kiểu MIME cần kiểm tra khớp với bộ xem thành phần Bonobo." + +#: ../mail/evolution-mail.schemas.in.in.h:37 +msgid "List of protocol names whose license has been accepted." +msgstr "Danh sách các tên giao thức có quyền đã chấp nhận." + +#: ../mail/evolution-mail.schemas.in.in.h:38 +msgid "Load images for HTML messages over http" +msgstr "Tải các ảnh cho thư HTML bằng giao thức HTTP" + +#: ../mail/evolution-mail.schemas.in.in.h:39 +msgid "" +"Load images for HTML messages over http(s). Possible values are: 0 - Never " +"load images off the net 1 - Load images in mail from contacts 2 - Always " +"load images off the net" +msgstr "" +"Tải các ảnh cho thư HTML bằng giao thức HTTP hay HTTPS. Giá trị có thể là:\n" +"0 - không bao giờ tải ảnh từ Mạng\n" +"1 - tải ảnh nếu người gởi có trong Sổ địa chỉ\n" +"2 - luôn luôn tải ảnh từ Mạng (không an toàn)" + +#: ../mail/evolution-mail.schemas.in.in.h:40 +msgid "Log filter actions" +msgstr "Ghi lưu các hành động lọc" + +#: ../mail/evolution-mail.schemas.in.in.h:41 +msgid "Log filter actions to the specified log file." +msgstr "Ghi lưu các hành động lọc vào tập tin bản ghi đã ghi rõ." + +#: ../mail/evolution-mail.schemas.in.in.h:42 +msgid "Logfile to log filter actions" +msgstr "Tập tin bản ghi để ghi lưu các hành động lọc." + +#: ../mail/evolution-mail.schemas.in.in.h:43 +msgid "Logfile to log filter actions." +msgstr "Tập tin bản ghi để ghi lưu các hành động lọc." + +#: ../mail/evolution-mail.schemas.in.in.h:44 +msgid "Mark as Seen after specified timeout" +msgstr "Đánh dấu là Đã xem, sau thời hạn đã ghi rõ." + +#: ../mail/evolution-mail.schemas.in.in.h:45 +msgid "Mark as Seen after specified timeout." +msgstr "Đánh dấu là Đã xem, sau thời hạn đã ghi rõ." + +#: ../mail/evolution-mail.schemas.in.in.h:46 +msgid "Mark citations in the message \"Preview\"" +msgstr "Đánh các trích dẫn trong thư « Xem thử »." + +#: ../mail/evolution-mail.schemas.in.in.h:47 +msgid "Mark citations in the message \"Preview\"." +msgstr "Đánh các trích dẫn trong thư « Xem thử »." + +#: ../mail/evolution-mail.schemas.in.in.h:48 +msgid "Message Window default height" +msgstr "Độ cao mặc định của Cửa sổ thư" + +#: ../mail/evolution-mail.schemas.in.in.h:49 +msgid "Message Window default width" +msgstr "Độ rộng mặc định của Cửa sổ thư" + +#: ../mail/evolution-mail.schemas.in.in.h:50 +msgid "Message-display style (normal, full headers, source)" +msgstr "Kiểu hiển thị thư (bình thường, dòng đầu đây đủ, mã nguồn)" + +#: ../mail/evolution-mail.schemas.in.in.h:51 +msgid "Minimum days between emptying the trash on exit" +msgstr "Số ngày tối thiểu giữa hai lần đổ Sọt Rác khi thoát." + +#: ../mail/evolution-mail.schemas.in.in.h:52 +msgid "Minimum time between emptying the trash on exit, in days." +msgstr "Thời gian tối thiểu giữa hai lần sổ Sọt Rác khi thoát, theo ngày." + +#: ../mail/evolution-mail.schemas.in.in.h:53 +msgid "New Mail Notify sound file" +msgstr "Tập tin âm thanh Thông báo Thư Mới" + +#: ../mail/evolution-mail.schemas.in.in.h:54 +msgid "New Mail Notify type" +msgstr "Kiểu Thông báo Thư Mới" + +#: ../mail/evolution-mail.schemas.in.in.h:55 +msgid "Prompt on empty subject" +msgstr "Nhắc khi chủ đề rỗng" + +#: ../mail/evolution-mail.schemas.in.in.h:56 +msgid "Prompt the user when he or she tries to expunge a folder." +msgstr "Nhắc người dùng khi họ cố xoá hẳn thư mục." + +#: ../mail/evolution-mail.schemas.in.in.h:57 +msgid "" +"Prompt the user when he or she tries to send a message without a Subject." +msgstr "Nhắc người dùng khi họ cố gởi thư chưa có Chủ đề." + +#: ../mail/evolution-mail.schemas.in.in.h:58 +msgid "Prompt when user expunges" +msgstr "Nhắc khi người dùng xoá hẳn" + +#: ../mail/evolution-mail.schemas.in.in.h:59 +msgid "Prompt when user only fills Bcc" +msgstr "Nhắc khi người dùng chỉ nhập Bcc" + +#: ../mail/evolution-mail.schemas.in.in.h:60 +msgid "Prompt when user tries to open 10 or more messages at once" +msgstr "Nhắc khi người dùng cố mở hơn 9 thư cùng lúc" + +#: ../mail/evolution-mail.schemas.in.in.h:61 +msgid "" +"Prompt when user tries to send HTML mail to recipients that may not want to " +"receive HTML mail." +msgstr "Nhắc khi người dùng cố gởi thư HTML cho liên lạc không muốn nhận HTML." + +#: ../mail/evolution-mail.schemas.in.in.h:62 +msgid "Prompt when user tries to send a message with no To or Cc recipients." +msgstr "" +"Nhắc khi người dùng cố gởi thư mà không có người nhận Cho (To) hay Chép Cho " +"(CC)." + +#: ../mail/evolution-mail.schemas.in.in.h:63 +msgid "Prompt when user tries to send unwanted HTML" +msgstr "Nhắc khi người dùng cố gởi thư dạng HTML không phải được muốn" + +#: ../mail/evolution-mail.schemas.in.in.h:64 +msgid "Recognize links in text and replace them." +msgstr "Nhận ra mọi liên kết trong văn bản và thay thế suốt." + +#: ../mail/evolution-mail.schemas.in.in.h:65 +msgid "Recognize smileys in text and replace them with images." +msgstr "Nhận dạng biểu tượng xác cảm trong văn bản và thay thế bằng ảnh." + +#: ../mail/evolution-mail.schemas.in.in.h:66 +msgid "Run junk test on incoming mail" +msgstr "Chạy kiểm tra thư Rác khi mọi thư gởi Đến" + +#: ../mail/evolution-mail.schemas.in.in.h:67 +msgid "Send HTML mail by default" +msgstr "Gởi thư mặc định dùng dạng HTML (không đệ nghị)" + +#: ../mail/evolution-mail.schemas.in.in.h:68 +msgid "Send HTML mail by default." +msgstr "Gởi thư mặc định dùng dạng HTML (không đệ nghị)." + +#: ../mail/evolution-mail.schemas.in.in.h:69 +msgid "Show Animations" +msgstr "Hiện hoạt cảnh" + +#: ../mail/evolution-mail.schemas.in.in.h:70 +msgid "Show animated images as animations." +msgstr "Hiện ảnh kiểu hoạt cảnh." + +#: ../mail/evolution-mail.schemas.in.in.h:71 +msgid "Show deleted messages (with a strike-through) in the message-list." +msgstr "" +"Hiển thị mọi thư bị xoá bỏ (kiểu gạch xuyên qua) trong danh sách các thư." + +#: ../mail/evolution-mail.schemas.in.in.h:72 +msgid "Show deleted messages in the message-list" +msgstr "Hiển thị thư bị xoá bỏ trong danh sách các thư." + +#: ../mail/evolution-mail.schemas.in.in.h:75 +msgid "Sound file to play when new mail arrives." +msgstr "Tập tin âm thanh cần phát khi nhận thư mới." + +#: ../mail/evolution-mail.schemas.in.in.h:76 +msgid "Specifies the type of New Mail Notification the user wishes to use." +msgstr "Ghi rõ kiểu Thông báo Thư Mới người dùng muốn dùng." + +#: ../mail/evolution-mail.schemas.in.in.h:77 +msgid "Spell check inline" +msgstr "Kiểm tra chính tả trực tiếp" + +#: ../mail/evolution-mail.schemas.in.in.h:78 +msgid "Subscribe dialog default height" +msgstr "Độ cao mặc định của hộp thoại đăng ký" + +#: ../mail/evolution-mail.schemas.in.in.h:79 +msgid "Subscribe dialog default width" +msgstr "Độ rộng mặc định của hộp thoại đăng ký" + +#: ../mail/evolution-mail.schemas.in.in.h:80 +msgid "Terminal font" +msgstr "Phông chữ thiết bị cuối" + +#: ../mail/evolution-mail.schemas.in.in.h:81 +msgid "The last time empty trash was run, in days since the epoch." +msgstr "Lần cuối cùng đã chạy đổ Sọt Rác, theo ngày từ kỷ nguyên bắt đầu." + +#: ../mail/evolution-mail.schemas.in.in.h:82 +msgid "The terminal font for mail display" +msgstr "Phông chữ thiết bị cuối để hiển thị thư" + +#: ../mail/evolution-mail.schemas.in.in.h:83 +msgid "The variable width font for mail display" +msgstr "Phông chữ độ rộng thay đổi để hiển thị thư" + +#: ../mail/evolution-mail.schemas.in.in.h:84 +msgid "" +"This key should contain a list of XML structures specifying custom headers, " +"and whether they are to be displayed. The format of the XML structure is <" +"header enabled> - set enabled if the header is to be displayed in the " +"mail view." +msgstr "" +"Khoá này nên chứa danh sách các cấu trúc XML ghi rõ dòng đầu tự chọn, và nếu " +"sẽ hiển thị chúng. Dang thức của cấu trúc XML là:\n" +"<header enabled>\n" +"(lập đã bật dòng đầu, nếu sẽ hiển thị dòng đầu đó trong khung xem thư)." + +#: ../mail/evolution-mail.schemas.in.in.h:85 +msgid "Thread the message list." +msgstr "Hiển thị mạch trong danh sách thư." + +#: ../mail/evolution-mail.schemas.in.in.h:86 +msgid "Thread the message-list" +msgstr "Hiển thị mạch trong danh sách thư." + +#: ../mail/evolution-mail.schemas.in.in.h:87 +msgid "Thread the message-list based on Subject" +msgstr "Hiển thị mạch trong danh sách thư, theo Chủ đề" + +#: ../mail/evolution-mail.schemas.in.in.h:88 +msgid "Timeout for marking message as Seen" +msgstr "Thời hạn Đánh dấu thư Đã xem." + +#: ../mail/evolution-mail.schemas.in.in.h:89 +msgid "Timeout for marking message as Seen." +msgstr "Thời hạn Đánh dấu thư Đã xem." + +#: ../mail/evolution-mail.schemas.in.in.h:90 +msgid "UID string of the default account." +msgstr "Chuỗi UID của tài khoản mặc định." + +#: ../mail/evolution-mail.schemas.in.in.h:91 +msgid "Use Spamassassin daemon and client" +msgstr "Sử dụng trình nền và khách Spamassassin" + +#: ../mail/evolution-mail.schemas.in.in.h:92 +msgid "Use Spamassassin daemon and client (spamc/spamd)" +msgstr "Sử dụng trình nền và khách Spamassassin (spamc/spamd)" + +#: ../mail/evolution-mail.schemas.in.in.h:93 +msgid "Use custom fonts" +msgstr "Sử dụng phông chữ tự chọn" + +#: ../mail/evolution-mail.schemas.in.in.h:94 +msgid "Use custom fonts for displaying mail" +msgstr "Sử dụng phông chữ tự chọn để hiển thị thư" + +#: ../mail/evolution-mail.schemas.in.in.h:95 +msgid "Use only local spam tests." +msgstr "Chỉ kiểm tra địa phương nếu thư là rác." + +#: ../mail/evolution-mail.schemas.in.in.h:96 +msgid "Use only the local spam tests (no DNS)." +msgstr "Chỉ kiểm tra địa phương nếu thư là rác (không có DNS)." + +#: ../mail/evolution-mail.schemas.in.in.h:97 +msgid "Variable width font" +msgstr "Phông chữ rộng biến" + +#: ../mail/evolution-mail.schemas.in.in.h:98 +msgid "View/Bcc menu item is checked" +msgstr "Đã chọn mục trình đơn Xem/Bcc." + +#: ../mail/evolution-mail.schemas.in.in.h:99 +msgid "View/Bcc menu item is checked." +msgstr "Đã chọn mục trình đơn Xem/Bcc." + +#: ../mail/evolution-mail.schemas.in.in.h:100 +msgid "View/Cc menu item is checked" +msgstr "Đã chọn mục trình đơn Xem/Cc." + +#: ../mail/evolution-mail.schemas.in.in.h:101 +msgid "View/Cc menu item is checked." +msgstr "Đã chọn mục trình đơn Xem/Cc." + +#: ../mail/evolution-mail.schemas.in.in.h:102 +msgid "View/From menu item is checked" +msgstr "Đã chọn mục trình đơn Xem/Từ." + +#: ../mail/evolution-mail.schemas.in.in.h:103 +msgid "View/From menu item is checked." +msgstr "Đã chọn mục trình đơn Xem/Từ." + +#: ../mail/evolution-mail.schemas.in.in.h:104 +msgid "View/PostTo menu item is checked" +msgstr "Đã chọn mục trình đơn Xem/Gởi tới." + +#: ../mail/evolution-mail.schemas.in.in.h:105 +msgid "View/PostTo menu item is checked." +msgstr "Đã chọn mục trình đơn Xem/Gởi tới." + +#: ../mail/evolution-mail.schemas.in.in.h:106 +msgid "View/ReplyTo menu item is checked" +msgstr "Đã chọn mục trình đơn Xem/Trả lời cho." + +#: ../mail/evolution-mail.schemas.in.in.h:107 +msgid "View/ReplyTo menu item is checked." +msgstr "Đã chọn mục trình đơn Xem/Trả lời cho." + +#: ../mail/evolution-mail.schemas.in.in.h:108 +msgid "" +"Whether or not to fall back on threading by subjects when the messages do " +"not contain In-Reply-To or References headers." +msgstr "" +"Có nên trở về xâu thư theo chủ đề khi thư không chứa dòng đầu In-Reply-To " +"(trả lời theo thư trước) hay References (tham chiếu đến)." + +#: ../mail/importers/elm-importer.c:192 ../mail/importers/elm-importer.c:193 +msgid "Importing Elm data" +msgstr "Đang nhập dữ liệu Elm" + +#: ../mail/importers/elm-importer.c:381 ../mail/importers/elm-importer.c:382 +msgid "Evolution Elm importer" +msgstr "Bộ nhập Elm vào Evolution" + +#: ../mail/importers/elm-importer.c:382 ../mail/importers/elm-importer.c:383 +msgid "Import mail from Elm." +msgstr "Đang nhập thư từ Elm" + +#: ../mail/importers/evolution-outlook-importer.c:131 +msgid "Destination folder:" +msgstr "Thư mục đích:" + +#: ../mail/importers/evolution-outlook-importer.c:134 +msgid "Select folder to import into" +msgstr "Chọn thư mục để nhập vào" + +#: ../mail/importers/evolution-mbox-importer.c:216 +msgid "Berkeley Mailbox (mbox)" +msgstr "Berkeley Mailbox (mbox)" + +#: ../mail/importers/evolution-mbox-importer.c:217 +msgid "Importer Berkeley Mailbox format folders" +msgstr "Bộ nhập thư mục dạng Berkeley Mailbox" + +#: ../mail/importers/mail-importer.c:230 ../shell/e-shell-importer.c:516 +#, c-format +msgid "Importing `%s'" +msgstr "Đang nhập « %s »" + +#: ../mail/importers/netscape-importer.c:1251 +msgid "Importing..." +msgstr "Đang nhập..." + +#: ../mail/importers/netscape-importer.c:1253 ../shell/e-shell-importer.c:523 +msgid "Please wait" +msgstr "Vui lòng chờ" + +#: ../mail/importers/mail-importer.c:144 +msgid "Importing mailbox" +msgstr "Đang nhập hộp thư..." + +#: ../mail/importers/mail-importer.c:377 ../mail/importers/mail-importer.c:373 +#, c-format +msgid "Scanning %s" +msgstr "Đang quét %s..." + +#: ../mail/importers/netscape-importer.c:73 +#, c-format +msgid "Priority Filter \"%s\"" +msgstr "Bộ lọc ưu tiên « %s »" + +#: ../mail/importers/netscape-importer.c:662 +msgid "" +"Some of your Netscape email filters are based on\n" +"email priorities, which are not used in Evolution.\n" +"Instead, Evolution provides scores in the range of\n" +"-3 to 3 that can be assigned to emails and filtered\n" +"accordingly.\n" +"\n" +"As a workaround, a set of filters called \"Priority Filter\"\n" +"was added that converts Netscape's email priorities into\n" +"Evolution's scores, and the affected filters use scores instead\n" +"of priorities. Check the imported filters to make sure\n" +"everything still works as intended." +msgstr "" +"Một số bộ lọc Netscape của bạn đựa vào độ ưu tiên thư,\n" +"mà Evolution không dùng. Thay vào đó, trình Evolution\n" +"cung cấp điểm từ -3 đến 3, thì có thể lọc thư theo điểm\n" +"đã gán vào nó.\n" +"\n" +"Để chỉnh sửa, đã thêm một tập hợp bộ lọc được gọi là\n" +"« Bộ lọc ưu tiên » mà chuyển đổi các ưu tiên Netscape\n" +"sang điểm Evolution, để các bộ lọc kiểu đó sẽ dùng điểm\n" +"thay vào ưu tiên. Hãy kiểm tra xem mỗi bộ lọc đã nhập\n" +"vẫn còn hoạt động cho đúng." + +#: ../mail/importers/netscape-importer.c:687 +#: ../mail/importers/netscape-importer.c:686 +msgid "" +"Some of your Netscape email filters use\n" +"the \"Ignore Thread\" or \"Watch Thread\"\n" +"feature, which is not supported in Evolution.\n" +"These filters will be dropped." +msgstr "" +"Một số bộ lọc Netscape của bạn dùng tín năng\n" +"« Bỏ qua mạch » hay « Theo dõi mạch » mà trình\n" +"Evolution không hỗ trợ. Sẽ bỏ qua những bộ lọc này." + +#: ../mail/importers/netscape-importer.c:704 +#: ../mail/importers/netscape-importer.c:703 +msgid "" +"Some of your Netscape email filters test the\n" +"body of emails for (in)equality to a given string,\n" +"which is not supported in Evolution. Those filters\n" +"were modified to test whether that string is or is not\n" +"contained in the message body." +msgstr "" +"Một số bộ lọc Netscape của bạn kiểm tra\n" +"nếu thân thư khớp (hay không khớp) một chuỗi đã cho,\n" +"mà trình Evolution không hỗ trợ. Đã sửa đổi\n" +"các bộ lọc đó để kiểm tra nếu thân thư\n" +"chứa chuỗi đó hay không." + +#: ../mail/importers/netscape-importer.c:1251 +#: ../mail/importers/netscape-importer.c:1250 +msgid "Evolution is importing your old Netscape data" +msgstr "Evolution đang nhập các dữ liệu cũ từ Netscape" + +#: ../mail/importers/netscape-importer.c:1708 +#: ../mail/importers/netscape-importer.c:1707 +msgid "Importing Netscape data" +msgstr "Đang nhập dữ liệu Netscape" + +#. #-#-#-#-# silky-0.5.3pre1.vi.po (silky-0.5.3pre1) #-#-#-#-# +#. I18N This is a window title +#: ../plug-ins/common/mosaic.c:722 ../glade/pyblio.glade.in.h:14 +#: src/settings.c:652 src/settings.c:657 prefs_gui.c:366 src/gui.c:362 +#: po/silky-channel.glade.h:23 +msgid "Settings" +msgstr "Thiết lập" + +#: ../mail/importers/netscape-importer.c:1913 +#: ../mail/importers/netscape-importer.c:1912 +msgid "Mail Filters" +msgstr "Bộ lọc thư" + +#: ../mail/importers/netscape-importer.c:1934 +#: ../mail/importers/netscape-importer.c:1933 +msgid "" +"Evolution has found Netscape mail files.\n" +"Would you like them to be imported into Evolution?" +msgstr "" +"Evolution đã tìm thấy những tập tin thư dạng Netscape.\n" +"Bạn có muốn nhập chúng vào Evolution không?" + +#: ../mail/importers/pine-importer.c:229 ../mail/importers/pine-importer.c:230 +msgid "Importing Pine data" +msgstr "Đang nhập dữ liệu Pine" + +#: ../mail/importers/pine-importer.c:433 ../mail/importers/pine-importer.c:434 +msgid "Evolution Pine importer" +msgstr "Bộ nhập Pine vào Evolution" + +#: ../mail/importers/pine-importer.c:434 ../mail/importers/pine-importer.c:435 +msgid "Import mail from Pine." +msgstr "Nhập thư từ Pine" + +#: ../mail/mail-autofilter.c:79 ../mail/mail-autofilter.c:78 +#, c-format +msgid "Mail to %s" +msgstr "Gởi thư chơ « %s »" + +#: ../mail/mail-autofilter.c:243 ../mail/mail-autofilter.c:282 +#: ../mail/mail-autofilter.c:242 ../mail/mail-autofilter.c:281 +#, c-format +msgid "Mail from %s" +msgstr "Thừ từ « %s »" + +#: ../mail/mail-autofilter.c:266 ../mail/mail-autofilter.c:265 +#, c-format +msgid "Subject is %s" +msgstr "Chủ để là « %s »" + +#: ../mail/mail-autofilter.c:301 ../mail/mail-autofilter.c:300 +#, c-format +msgid "%s mailing list" +msgstr "Hộp thư chung « %s »" + +#: ../mail/mail-autofilter.c:372 ../mail/mail-autofilter.c:369 +msgid "Add Filter Rule" +msgstr "Thêm quy tắc lọc" + +#: ../mail/mail-component.c:510 ../mail/mail-component.c:508 +#, c-format +msgid "%d deleted" +msgid_plural "%d deleted" +msgstr[0] "%d bị xoá bỏ" + +#: ../mail/mail-component.c:512 ../mail/mail-component.c:510 +#, c-format +msgid "%d junk" +msgid_plural "%d junk" +msgstr[0] "%d rác" + +#: ../mail/mail-component.c:535 ../mail/mail-component.c:533 +#, c-format +msgid "%d draft" +msgid_plural "%d draft" +msgstr[0] "%d nháp" + +#: ../mail/mail-component.c:537 ../mail/mail-component.c:535 +#, c-format +msgid "%d sent" +msgid_plural "%d sent" +msgstr[0] "%d đã gởi" + +#: ../mail/mail-component.c:539 ../mail/mail-component.c:537 +#, c-format +msgid "%d unsent" +msgid_plural "%d unsent" +msgstr[0] "%d chưa gởi" + +#: ../mail/mail-component.c:543 ../mail/mail-component.c:541 +#, c-format +msgid "%d total" +msgid_plural "%d total" +msgstr[0] "%d tổng cộng" + +#: ../mail/mail-component.c:545 ../mail/mail-component.c:543 +#, c-format +msgid ", %d unread" +msgid_plural ", %d unread" +msgstr[0] ", %d chưa đọc" + +#: ../mail/mail-component.c:766 ../mail/mail-component.c:764 +msgid "New Mail Message" +msgstr "Thư mới" + +#: ../mail/mail-component.c:767 ../mail/mail-component.c:765 +msgid "_Mail Message" +msgstr "_Thư" + +#: ../mail/mail-component.c:768 ../mail/mail-component.c:766 +msgid "Compose a new mail message" +msgstr "Biên soạn thư mới" + +#: ../mail/mail-component.c:774 ../mail/mail-component.c:772 +msgid "New Mail Folder" +msgstr "Hộp thư mới" + +#: ../mail/mail-component.c:775 ../mail/mail-component.c:773 +msgid "Mail _Folder" +msgstr "_Hộp thư" + +#: ../mail/mail-component.c:776 ../mail/mail-component.c:774 +msgid "Create a new mail folder" +msgstr "Tạo hộp thư mới" + +#: ../mail/mail-component.c:920 ../mail/mail-component.c:918 +msgid "Failed upgrading Mail settings or folders." +msgstr "Không cập nhật thiết lập hay thư mục Thư được." + +#: ../mail/mail-config.glade.h:2 +msgid " Ch_eck for Supported Types " +msgstr "_Kiểm tra kiểu được hỗ trợ " + +#: ../mail/mail-config.glade.h:4 +msgid "SSL is not supported in this build of Evolution" +msgstr "Phiên bản Evolution đang dùng không hỗ trợ SSL" + +#: ../mail/mail-config.glade.h:5 +msgid "Sig_natures" +msgstr "Chữ _ký" + +#: ../mail/mail-config.glade.h:6 +msgid "_Languages" +msgstr "_Ngôn ngữ " + +#: ../mail/mail-config.glade.h:7 +msgid "This will make the the filter more reliable, but slower" +msgstr "Việc này giúp bộ lọc đáng tin hơn, nhưng chậm hơn" + +#: ../mail/mail-config.glade.h:8 +msgid "Account Information" +msgstr "Thông tin tài khoản" + +#: ../mail/mail-config.glade.h:10 +msgid "Authentication Type" +msgstr "Kiểu xác thực" + +#: ../mail/mail-config.glade.h:11 +msgid "Authentication" +msgstr "Xác thực" + +#: ../mail/mail-config.glade.h:12 +msgid "Composing Messages" +msgstr "Soạn thư" + +#: ../mail/mail-config.glade.h:13 +msgid "Configuration" +msgstr "Cấu hình" + +#: ../mail/mail-config.glade.h:14 +msgid "Default Behavior" +msgstr "Hành vi mặc định" + +#: ../mail/mail-config.glade.h:15 +msgid "Delete Mail" +msgstr "Xoá bỏ thư" + +#: ../mail/mail-config.glade.h:16 +msgid "Displayed Mail _Headers" +msgstr "_Dòng đầu thư được hiển thị" + +#: ../mail/mail-config.glade.h:17 +msgid "Filter Options" +msgstr "Tùy chọn bộ lọc" + +#: ../mail/mail-config.glade.h:19 +msgid "Labels and Colors" +msgstr "Nhãn và màu" + +#: ../mail/mail-config.glade.h:20 +msgid "Loading Images" +msgstr "Tải ảnh" + +#: ../mail/mail-config.glade.h:21 +msgid "Message Display" +msgstr "Hiển thị thư" + +#: ../mail/mail-config.glade.h:22 +msgid "Message Fonts" +msgstr "Phông chữ thư" + +#: ../mail/mail-config.glade.h:23 +msgid "Message Receipts" +msgstr "Thông báo đã đọc thư" + +#: ../mail/mail-config.glade.h:24 +msgid "New Mail Notification" +msgstr "Thông báo nhận thư mới" + +#: ../mail/mail-config.glade.h:25 +msgid "Optional Information" +msgstr "Thông tin tùy chọn" + +#: ../mail/mail-config.glade.h:26 +msgid "Options" +msgstr "Tùy chọn" + +#: ../mail/mail-config.glade.h:27 +msgid "Pretty Good Privacy (PGP/GPG)" +msgstr "Riêng tư hơi tốt (PGP/GPG)" + +#: ../mail/mail-config.glade.h:28 +msgid "Printed Fonts" +msgstr "Phông chữ in" + +#: ../mail/mail-config.glade.h:29 +msgid "Required Information" +msgstr "Thông tin bắt buộc" + +#: ../mail/mail-config.glade.h:30 +msgid "Secure MIME (S/MIME)" +msgstr "MIME an toàn (S/MIME)" + +#: ../mail/mail-config.glade.h:31 +msgid "Security" +msgstr "Bảo mật" + +#: ../mail/mail-config.glade.h:32 +msgid "Sent and Draft Messages" +msgstr "Thư đã gởi và thư nháp" + +#: ../mail/mail-config.glade.h:33 +msgid "Server Configuration" +msgstr "Cấu hình máy phục vụ" + +#: ../mail/mail-config.glade.h:35 +msgid "Account Management" +msgstr "Quản lý tài khoản" + +#: ../mail/mail-config.glade.h:36 +msgid "Add Ne_w Signature..." +msgstr "_Thêm chữ ký mới..." + +#: ../mail/mail-config.glade.h:37 +msgid "Add _Script" +msgstr "Thêm tập _lệnh" + +#: ../mail/mail-config.glade.h:38 +msgid "Al_ways sign outgoing messages when using this account" +msgstr "_Luôn ký tên lên các thư cần gởi đi khi dùng tài khoản này" + +#: ../mail/mail-config.glade.h:39 +msgid "Also encrypt to sel_f when sending encrypted mail" +msgstr "_Cũng tự mật mã khi gởi thư mật mã" + +#: ../mail/mail-config.glade.h:40 +msgid "Alway_s carbon-copy (cc) to:" +msgstr "Luôn _Chép Cho (Cc) tới:" + +#: ../mail/mail-config.glade.h:41 +msgid "Always _blind carbon-copy (bcc) to:" +msgstr "Luôn _Bí mật Chép Cho (Bcc) tới:" + +#: ../mail/mail-config.glade.h:42 +msgid "Always _trust keys in my keyring when encrypting" +msgstr "Luôn _tin khoá trong dây khoá tôi khi mật mã hóa" + +#: ../mail/mail-config.glade.h:43 +msgid "Always encrypt to _myself when sending encrypted mail" +msgstr "_Luôn tự mật mã khi gởi thư được mật mã" + +#: ../mail/mail-config.glade.h:44 +msgid "Attach original message" +msgstr "Đính kèm thư gốc" + +#: ../mail/mail-config.glade.h:46 +msgid "Automatically insert _smiley images" +msgstr "Tự động chèn _biểu tượng xúc cảm" + +#: ../mail/mail-config.glade.h:47 +msgid "Baltic (ISO-8859-13)" +msgstr "Ban-tích (ISO-8859-13)" + +#: ../mail/mail-config.glade.h:48 +msgid "Baltic (ISO-8859-4)" +msgstr "Ban-tích (ISO-8859-4)" + +#: ../mail/mail-config.glade.h:49 +msgid "Beep w_hen new mail arrives" +msgstr "_Kêu bíp khi nhận thư mới" + +#: ../mail/mail-config.glade.h:50 +msgid "C_haracter set:" +msgstr "Bộ _ký tự :" + +#: ../mail/mail-config.glade.h:51 +msgid "Ch_eck for Supported Types " +msgstr "_Kiểm tra kiểu được hỗ trợ " + +#: ../mail/mail-config.glade.h:52 +msgid "Check in_coming mail for junk" +msgstr "Kiểm tra thư mới _đến là rác" + +#: ../mail/mail-config.glade.h:53 +msgid "Check spelling while I _type" +msgstr "Kiểm tra chính tả khi đang _gõ" + +#: ../mail/mail-config.glade.h:54 +msgid "Checks incoming mail messages to be Junk" +msgstr "Kiểm tra nếu thư đã gởi đến là thư Rác" + +#: ../mail/mail-config.glade.h:55 +msgid "Cle_ar" +msgstr "_Xoá" + +#: ../mail/mail-config.glade.h:56 +msgid "Clea_r" +msgstr "Xó_a" + +#: ../mail/mail-config.glade.h:57 +msgid "Color for _misspelled words:" +msgstr "Màu từ gõ _sai:" + +#: ../mail/mail-config.glade.h:59 +msgid "Confirm _when expunging a folder" +msgstr "_Xác nhận khi xoá hẳn thư mục" + +#: ../mail/mail-config.glade.h:60 +msgid "" +"Congratulations, your mail configuration is complete.\n" +"\n" +"You are now ready to send and receive email \n" +"using Evolution. \n" +"\n" +"Click \"Apply\" to save your settings." +msgstr "" +"Xin chúc mừng, bạn đã hoàn tất quá trình cấu hình thư.\n" +"\n" +"Từ bây giờ bạn có thể gởi và nhận thư bằng Evolution.\n" +"\n" +"Hãu nhấn « Áp dụng » để lưu các thiết lập." + +#: ../mail/mail-config.glade.h:66 +msgid "De_fault" +msgstr "_Mặc định" + +#: ../mail/mail-config.glade.h:67 +msgid "Default character e_ncoding:" +msgstr "Bộ _ký tự mặc định:" + +#: ../mail/mail-config.glade.h:70 +msgid "Digitally _sign outgoing messages (by default)" +msgstr "_Luôn ký số lên các thư cần gởi đi (theo mặc định)" + +#: ../mail/mail-config.glade.h:71 +msgid "Do not quote original message" +msgstr "Không trích dẫn thư gốc" + +#: ../mail/mail-config.glade.h:73 +msgid "Drafts _Folder:" +msgstr "Thư mục _Nháp:" + +#: ../mail/mail-config.glade.h:75 +msgid "Email Accounts" +msgstr "Tài khoản thư" + +#: ../mail/mail-config.glade.h:76 ../pan/dialogs/dialog-newuser.c:273 +msgid "Email _Address:" +msgstr "_Địa chỉ thư :" + +#: ../mail/mail-config.glade.h:77 +msgid "Empty trash folders on e_xit" +msgstr "_Đổ các thư mục Rác khi thoát" + +#: ../mail/mail-config.glade.h:78 +msgid "Encry_ption certificate:" +msgstr "Chứng nhận _mật mã:" + +#: ../mail/mail-config.glade.h:79 +msgid "Encrypt out_going messages (by default)" +msgstr "_Mật mã mọi thư cần gởi đi (theo mặc định)" + +#: ../mail/mail-config.glade.h:81 ../mail/mail-config.glade.h:82 +msgid "Fi_xed-width:" +msgstr "Độ rộng cố _định:" + +#: ../mail/mail-config.glade.h:82 ../mail/mail-config.glade.h:83 +msgid "Font Properties" +msgstr "Thuộc tính phông chữ" + +#: ../mail/mail-config.glade.h:83 ../mail/mail-config.glade.h:84 +msgid "Format messages in _HTML" +msgstr "Định dạng thư bằng _HTML" + +#: ../mail/mail-config.glade.h:85 ../mail/mail-config.glade.h:86 +msgid "HTML Mail" +msgstr "Thư HTML" + +#: ../mail/mail-config.glade.h:86 ../mail/mail-config.glade.h:87 +#: ../pan/gui-notebook.c:56 ../pan/prefs.c:1623 +msgid "Headers" +msgstr "Dòng đầu" + +#: ../mail/mail-config.glade.h:87 ../mail/mail-config.glade.h:88 +msgid "Highlight _quotations with" +msgstr "Tô sang _trích dẫn bằng" + +#: ../mail/mail-config.glade.h:88 ../mail/mail-config.glade.h:89 +msgid "I_nclude remote tests" +msgstr "Cũ_ng thử từ xa" + +#: ../mail/mail-config.glade.h:93 ../mail/mail-config.glade.h:94 +msgid "Languages Table" +msgstr "Bảng ngôn ngữ" + +#: ../mail/mail-config.glade.h:95 ../src/united-states-of-bug-buddy.c:59 +#: ../mail/mail-config.glade.h:96 +msgid "Mail Configuration" +msgstr "Cấu hình thư" + +#: ../mail/mail-config.glade.h:96 ../mail/mail-config.glade.h:97 +msgid "Mail Headers Table" +msgstr "Bảng dòng đầu thư" + +#: ../mail/mail-config.glade.h:98 ../mail/mail-config.glade.h:99 +msgid "Mailbox location" +msgstr "Địa điểm hộp thư" + +#: ../mail/mail-config.glade.h:99 ../mail/mail-config.glade.h:100 +msgid "Message Composer" +msgstr "Bộ soạn thảo thư" + +#: ../mail/mail-config.glade.h:100 ../mail/mail-config.glade.h:101 +msgid "" +"Note: you will not be prompted for a password until you connect for the " +"first time" +msgstr "Ghi chú : sẽ không nhắc bạn nhập mật khẩu tới khi kết nối lần đầu." + +#: ../mail/mail-config.glade.h:101 ../mail/mail-config.glade.h:102 +msgid "Or_ganization:" +msgstr "Tổ _chức:" + +#: ../mail/mail-config.glade.h:102 ../mail/mail-config.glade.h:103 +msgid "PGP/GPG _Key ID:" +msgstr "ID _khoá PGP/GPG:" + +#: ../mail/mail-config.glade.h:105 ../mail/mail-config.glade.h:106 +msgid "Play sound file when new mail arri_ves" +msgstr "_Phát tập tin âm thanh khi nhận thư mới" + +#: ../mail/mail-config.glade.h:106 ../mail/mail-config.glade.h:107 +msgid "" +"Please enter a descriptive name for this account in the space below.\n" +"This name will be used for display purposes only." +msgstr "" +"Hãy nhập một tên diễn tả cho tài khoản này vào trường bên dưới.\n" +"Chỉ được dùng tên này với mục đích hiển thị thôi." + +#: ../mail/mail-config.glade.h:108 ../mail/mail-config.glade.h:109 +msgid "" +"Please enter information about the way you will send mail. If you are not " +"sure, ask your system administrator or Internet Service Provider." +msgstr "" +"Hãy nhập thông tin về cách bạn sẽ gởi thư. Nếu bạn không chắc, hãy hỏi quản " +"trị hệ thống hoặc ISP (nhà cung cấp dịch vụ Mạng) của bạn." + +#: ../mail/mail-config.glade.h:109 ../mail/mail-config.glade.h:110 +msgid "" +"Please enter your name and email address below. The \"optional\" fields " +"below do not need to be filled in, unless you wish to include this " +"information in email you send." +msgstr "" +"Vui lòng nhập tên và địa chỉ thư điện từ vào bên dưới. Trường « tùy chọn » " +"bên dưới không cần phải được chọn, trừ khi bạn muốn gồm thông tin đó vào thư " +"bạn cần gởi." + +#: ../mail/mail-config.glade.h:110 ../mail/mail-config.glade.h:111 +msgid "Please select among the following options" +msgstr "Hãy chọn từ các tuỳ chọn sau" + +#: ../mail/mail-config.glade.h:111 ../mail/mail-config.glade.h:112 +msgid "Pr_ompt when sending messages with only Bcc recipients defined" +msgstr "_Nhắc khi gởi thư mà chỉ có người nhận _Bí mật Chép Cho (Bcc)" + +#: ../mail/mail-config.glade.h:112 ../mail/mail-config.glade.h:113 +msgid "Quote original message" +msgstr "Trích dẫn thư gốc" + +#: ../mail/mail-config.glade.h:114 ../mail/mail-config.glade.h:115 +msgid "Re_member password" +msgstr "_Nhớ mật khẩu" + +#: ../mail/mail-config.glade.h:115 ../mail/mail-config.glade.h:116 +msgid "Re_ply-To:" +msgstr "T_rả lời cho:" + +#: ../mail/mail-config.glade.h:117 ../mail/mail-config.glade.h:118 +msgid "Remember _password" +msgstr "_Nhớ mật khẩu" + +#: ../mail/mail-config.glade.h:118 ../mail/mail-config.glade.h:119 +msgid "S_elect..." +msgstr "_Chọn..." + +#: ../mail/mail-config.glade.h:119 ../mail/mail-config.glade.h:120 +msgid "S_tandard Font:" +msgstr "Phông chữ _chuẩn:" + +#: ../mail/mail-config.glade.h:120 ../mail/mail-config.glade.h:121 +msgid "Se_lect..." +msgstr "C_họn..." + +#: ../mail/mail-config.glade.h:122 ../mail/mail-config.glade.h:123 +msgid "Select HTML fixed width font" +msgstr "Chọn phông chữ HTML độ rộng cứng" + +#: ../mail/mail-config.glade.h:123 ../mail/mail-config.glade.h:124 +msgid "Select HTML fixed width font for printing" +msgstr "Chọn phông chữ HTML độ rộng cứng để in" + +#: ../mail/mail-config.glade.h:124 ../mail/mail-config.glade.h:125 +msgid "Select HTML variable width font" +msgstr "Chọn phông chữ độ rộng biến HTML" + +#: ../mail/mail-config.glade.h:125 ../mail/mail-config.glade.h:126 +msgid "Select HTML variable width font for printing" +msgstr "Chọn phông HTML độ rộng thay đổi để in" + +#: ../mail/mail-config.glade.h:126 ../gok/gok-page-feedbacks.c:875 +msgid "Select sound file" +msgstr "Chọn tập tin âm thanh" + +#: ../mail/mail-config.glade.h:127 +msgid "Send message receipts:" +msgstr "Gởi thông báo đã đọc thư :" + +#: ../mail/mail-config.glade.h:129 ../pan/prefs.c:1888 +msgid "Sending Mail" +msgstr "Gởi thư" + +#: ../mail/mail-config.glade.h:130 +msgid "Sent _Messages Folder:" +msgstr "Thư mục thư Đã _gởi:" + +#: ../mail/mail-config.glade.h:131 +msgid "Ser_ver requires authentication" +msgstr "Máy phục vụ cần thiết _xác thực" + +#: ../mail/mail-config.glade.h:132 +msgid "Server _Type: " +msgstr "_Kiểu máy phục vụ :" + +#: ../mail/mail-config.glade.h:133 +msgid "Sig_ning certificate:" +msgstr "Chứng nhận _ký tên:" + +#: ../mail/mail-config.glade.h:134 +msgid "Signat_ure:" +msgstr "Chữ _ký:" + +#: ../mail/mail-config.glade.h:135 +msgid "Signatures" +msgstr "Chữ ký" + +#: ../mail/mail-config.glade.h:136 +msgid "Signatures Table" +msgstr "Bảng chữ ký" + +#: ../mail/mail-config.glade.h:137 +msgid "Specify _filename:" +msgstr "Xác định _tên tập tin:" + +#: ../mail/mail-config.glade.h:138 +msgid "Spell Checking" +msgstr "Kiểm tra chính tả" + +#: ../mail/mail-config.glade.h:139 +msgid "T_erminal Font:" +msgstr "Phông chữ _thiết bị cuối:" + +#: ../mail/mail-config.glade.h:140 +msgid "T_ype: " +msgstr "_Kiểu : " + +#: ../mail/mail-config.glade.h:141 +msgid "" +"The list of languages here reflects only the languages for which you have a " +"dictionary installed." +msgstr "" +"Danh sách ngôn ngữ phản ánh chỉ những ngôn ngữ mà bạn có cài đặt từ điển." + +#: ../mail/mail-config.glade.h:142 +msgid "" +"The output of this script will be used as your\n" +"signature. The name you specify will be used\n" +"for display purposes only. " +msgstr "" +"Đầu ra của tập lệnh này sẽ được dùng\n" +"như là chữ ký bạn.\n" +"Tên bạn xác định sẽ chỉ được dùng\n" +"cho mục đích hiển thị." + +#: ../mail/mail-config.glade.h:146 +msgid "" +"Type the name by which you would like to refer to this account.\n" +"For example: \"Work\" or \"Personal\"" +msgstr "" +"Hãy gõ tên mà bạn muốn dùng cho tài khoản này.\n" +"Ví dụ : « Chỗ làm » hoặc « Ở nhà »." + +#: ../mail/mail-config.glade.h:148 +msgid "User_name:" +msgstr "T_ên người dùng:" + +#: ../mail/mail-config.glade.h:149 +msgid "V_ariable-width:" +msgstr "Độ rộng th_ay đổi:" + +#: ../mail/mail-config.glade.h:150 +msgid "" +"Welcome to the Evolution Mail Configuration Assistant.\n" +"\n" +"Click \"Forward\" to begin. " +msgstr "" +"Chào mừng dùng Phụ tá cấu hình thư Evolution.\n" +"\n" +"Hãy nhấn « Tiếp » để bắt đầu." + +#: ../mail/mail-config.glade.h:154 +msgid "_Add Signature" +msgstr "Thêm chữ _ký" + +#: ../mail/mail-config.glade.h:155 +msgid "_Always load images from the Internet" +msgstr "_Luôn tải ảnh xuống Mạng (không đệ nghị)" + +#: ../mail/mail-config.glade.h:156 +msgid "_Do not notify me when new mail arrives" +msgstr "Đừn_g thông báo tôi khi nhận thư mới" + +#: ../mail/mail-config.glade.h:157 +msgid "_Don't sign meeting requests (for Outlook compatibility)" +msgstr "_Không ký tên yêu cầu họp (để tương thích với trình Outlook)" + +#: ../mail/mail-config.glade.h:159 +msgid "_Forward style:" +msgstr "Kiểu dáng _chuyển tiếp:" + +#: ../mail/mail-config.glade.h:161 +msgid "_Load images in mail from contacts" +msgstr "_Tải ảnh trong thư từ liên lạc" + +#: ../mail/mail-config.glade.h:162 +msgid "_Make this my default account" +msgstr "Chọn làm tài khoản này _mặc định" + +#: ../mail/mail-config.glade.h:163 +msgid "_Mark messages as read after" +msgstr "Đánh dấu thư đã đọ_c sau" + +#: ../mail/mail-config.glade.h:165 +msgid "_Never load images from the Internet" +msgstr "_Không bao giờ tải ảnh từ Mạng" + +#: ../mail/mail-config.glade.h:166 +msgid "_Path:" +msgstr "Đường _dẫn:" + +#: ../mail/mail-config.glade.h:167 +msgid "_Prompt when sending HTML messages to contacts that don't want them" +msgstr "_Nhắc khi gởi thư HTML cho các liên lạc không muốn nhận HTML" + +#: ../mail/mail-config.glade.h:168 +msgid "_Prompt when sending messages with an empty subject line" +msgstr "_Nhắc khi gởi thư không có chủ đề" + +#: ../mail/mail-config.glade.h:169 +msgid "_Reply style:" +msgstr "_Kiểu dáng trả lời:" + +#: ../mail/mail-config.glade.h:170 +msgid "_Script:" +msgstr "Tập _lệnh:" + +#: ../mail/mail-config.glade.h:172 +msgid "_Show animated images" +msgstr "_Hiện hoạt cảnh" + +#: ../mail/mail-config.glade.h:173 +msgid "_Use Secure Connection:" +msgstr "Dùng kết nối _an toàn:" + +#: ../mail/mail-config.glade.h:174 +msgid "_Use the same fonts as other applications" +msgstr "_Dùng cùng những phông chữ với các ứng dụng khác" + +#: ../mail/mail-config.glade.h:175 +msgid "color" +msgstr "màu" + +#: ../providers/evolution/gda-calendar-model.c:68 +msgid "description" +msgstr "mô tả" + +#: ../mail/mail-dialogs.glade.h:2 +msgid "Search Folder Sources" +msgstr "Nguồn thư mục tìm kiếm" + +#: ../mail/mail-dialogs.glade.h:3 +msgid "Digital Signature" +msgstr "Chữ ký số" + +#: ../mail/mail-dialogs.glade.h:4 +msgid "Encryption" +msgstr "Mật mã" + +#: ../mail/mail-dialogs.glade.h:5 ../gnomecard/gnomecard.glade.h:2 +msgid "Case _sensitive" +msgstr "_Phân biệt hoa/thường" + +#: ../mail/mail-dialogs.glade.h:6 ../mail/message-tags.glade.h:2 +msgid "Co_mpleted" +msgstr "_Hoàn tất" + +#: ../mail/mail-dialogs.glade.h:8 ../shell/eggfindbar.c:300 +#: ../pan/grouplist.c:1033 +msgid "F_ind:" +msgstr "_Tìm:" + +#: ../mail/mail-dialogs.glade.h:9 +msgid "Find in Message" +msgstr "Tìm trong thư" + +#: ../mail/mail-dialogs.glade.h:10 ../mail/message-tag-followup.c:297 +#: ../mail/message-tags.glade.h:3 ../mail/message-tag-followup.c:295 +msgid "Flag to Follow Up" +msgstr "Đặt cờ để theo dõi tiếp" + +#: ../mail/mail-dialogs.glade.h:11 +msgid "Folder Subscriptions" +msgstr "Đăng ký thư mục" + +#: ../mail/mail-dialogs.glade.h:12 +msgid "License Agreement" +msgstr "Điều kiện Quyền" + +#: ../mail/mail-dialogs.glade.h:13 +msgid "None Selected" +msgstr "Chưa chọn" + +#: ../mail/mail-dialogs.glade.h:14 +msgid "S_erver:" +msgstr "_Máy phục vụ :" + +#: ../mail/mail-dialogs.glade.h:15 +msgid "Security Information" +msgstr "Thông tin bảo mật" + +#: ../mail/mail-dialogs.glade.h:17 ../mail/message-tags.glade.h:4 +msgid "" +"The messages you have selected for follow up are listed below.\n" +"Please select a follow up action from the \"Flag\" menu." +msgstr "" +"Các thư mà bạn đã chọn để theo dõi tiếp thì được liệt kê bên dưới.\n" +"Hãy chọn một hành động theo dõi tiếp từ trình đơn « Cờ »." + +#: ../mail/mail-dialogs.glade.h:19 +msgid "_Accept License" +msgstr "_Chấp nhận các điều kiện này" + +#: ../mail/mail-dialogs.glade.h:20 ../mail/message-tags.glade.h:6 +msgid "_Due By:" +msgstr "Đến _hạn:" + +#: ../mail/mail-dialogs.glade.h:21 ../mail/message-tags.glade.h:7 +msgid "_Flag:" +msgstr "_Cờ :" + +#: ../mail/mail-dialogs.glade.h:23 +msgid "_Tick this to accept the license agreement" +msgstr "" +"Hãy _Đánh dấu trong hộp chọn này để chấp nhận các điều kiện quyền phép." + +#: ../mail/mail-dialogs.glade.h:25 +msgid "specific folders only" +msgstr "chỉ những thư mục dứt khoát thôi" + +#: ../mail/mail-dialogs.glade.h:26 +msgid "with all active remote folders" +msgstr "với mọi thư mục hoạt động từ xa" + +#: ../mail/mail-dialogs.glade.h:27 +msgid "with all local and active remote folders" +msgstr "với mọi thư mục hoạt động từ xa và cục bộ đều" + +#: ../mail/mail-dialogs.glade.h:28 +msgid "with all local folders" +msgstr "với mọi thư mục cục bộ" + +#: ../mail/mail-folder-cache.c:860 ../mail/mail-folder-cache.c:853 +#, c-format +msgid "Pinging %s" +msgstr "Đang « ping » %s..." + +#: ../mail/mail-ops.c:103 +msgid "Filtering Folder" +msgstr "Đang lọc thư mục..." + +#: ../mail/mail-ops.c:264 ../mail/mail-ops.c:263 +msgid "Fetching Mail" +msgstr "Đang lấy thư..." + +#. sending mail, filtering failed +#: ../mail/mail-ops.c:564 ../mail/mail-ops.c:563 +#, c-format +msgid "Failed to apply outgoing filters: %s" +msgstr "Không áp dụng bộ lọc gởi đi được: %s" + +#: ../mail/mail-ops.c:576 ../mail/mail-ops.c:605 ../mail/mail-ops.c:575 +#: ../mail/mail-ops.c:604 +#, c-format +msgid "" +"Failed to append to %s: %s\n" +"Appending to local `Sent' folder instead." +msgstr "" +"Lỗi phụ thêm vào %s: %s\n" +"Thì phụ thêm vào thư mục « Đã gởi » thay vào đó." + +#: ../mail/mail-ops.c:622 ../mail/mail-ops.c:621 +#, c-format +msgid "Failed to append to local `Sent' folder: %s" +msgstr "Lỗi thêm vào thư mục « Đã gởi » cục bộ : %s" + +#: ../mail/mail-ops.c:734 ../mail/mail-ops.c:733 +#, c-format +msgid "Sending message %d of %d" +msgstr "Đang gởi thư %d trên %d..." + +#: ../mail/mail-ops.c:759 ../mail/mail-ops.c:758 +#, c-format +msgid "Failed to send %d of %d messages" +msgstr "Việc gởi %d trên %d thư bị lỗi." + +#: ../mail/mail-ops.c:761 ../mail/mail-send-recv.c:613 ../mail/mail-ops.c:760 +#: ../camel/camel-gpg-context.c:803 ../camel/camel-gpg-context.c:1000 +#: ../camel/providers/nntp/camel-nntp-store.c:1276 +msgid "Cancelled." +msgstr "Bị thôi" + +#: ../mail/mail-ops.c:763 ../mail/mail-ops.c:762 +msgid "Complete." +msgstr "Hoàn tất." + +#: ../mail/mail-ops.c:860 ../mail/mail-ops.c:859 +msgid "Saving message to folder" +msgstr "Đang lưu thư vào thư mục..." + +#: ../mail/mail-ops.c:945 ../mail/mail-ops.c:944 +#, c-format +msgid "Moving messages to %s" +msgstr "Đang chuyển thư tới %s..." + +#: ../mail/mail-ops.c:945 ../mail/mail-ops.c:944 +#, c-format +msgid "Copying messages to %s" +msgstr "Đang sao chép thư vào « %s »" + +#: ../mail/mail-ops.c:1168 ../mail/mail-ops.c:1167 +msgid "Forwarded messages" +msgstr "Thư đã chuyển tiếp" + +#: ../mail/mail-ops.c:1211 ../mail/mail-ops.c:1210 +#, c-format +msgid "Opening folder %s" +msgstr "Đang mở thư mục « %s »" + +#: ../mail/mail-ops.c:1283 ../mail/mail-ops.c:1282 +#, c-format +msgid "Opening store %s" +msgstr "Đang mở kho « %s »" + +#: ../mail/mail-ops.c:1361 ../mail/mail-ops.c:1360 +#, c-format +msgid "Removing folder %s" +msgstr "Đang gở bỏ thư mục « %s »" + +#: ../mail/mail-ops.c:1455 ../mail/mail-ops.c:1454 +#, c-format +msgid "Storing folder '%s'" +msgstr "Đang cất giữ thư mục « %s »" + +#: ../mail/mail-ops.c:1520 ../mail/mail-ops.c:1519 +#, c-format +msgid "Expunging and storing account '%s'" +msgstr "Đang xoá hẳn và cất giữ tài khoản « %s »" + +#: ../mail/mail-ops.c:1521 ../mail/mail-ops.c:1520 +#, c-format +msgid "Storing account '%s'" +msgstr "Đang cất giữ tài khoản « %s »" + +#: ../mail/mail-ops.c:1576 +msgid "Refreshing folder" +msgstr "Đang cập nhật thư mục" + +#: ../mail/mail-ops.c:1612 ../mail/mail-ops.c:1663 ../mail/mail-ops.c:1611 +#: ../mail/mail-ops.c:1662 +msgid "Expunging folder" +msgstr "Đang xoá hẳn thư mục" + +#: ../mail/mail-ops.c:1660 ../mail/mail-ops.c:1659 +#, c-format +msgid "Emptying trash in '%s'" +msgstr "Đang đổ sọt rác trong « %s »" + +#: ../mail/mail-ops.c:1661 ../mail/mail-ops.c:1660 +msgid "Local Folders" +msgstr "Thư mục cục bộ" + +#: ../mail/mail-ops.c:1744 ../mail/mail-ops.c:1743 +#, c-format +msgid "Retrieving message %s" +msgstr "Đang gọi thư « %s »" + +#: ../mail/mail-ops.c:1854 ../mail/mail-ops.c:1853 +#, c-format +msgid "Retrieving %d message" +msgid_plural "Retrieving %d message" +msgstr[0] "Đang gọi %d thư" + +#: ../mail/mail-ops.c:1940 ../mail/mail-ops.c:1939 +#, c-format +msgid "Saving %d message" +msgid_plural "Saving %d message" +msgstr[0] "Đang lưu %d thư" + +#: ../mail/mail-ops.c:1990 ../mail/mail-ops.c:1989 +#, c-format +msgid "" +"Unable to create output file: %s\n" +" %s" +msgstr "" +"Không thể tạo tập tin xuất: %s\n" +" %s" + +#: ../mail/mail-ops.c:2018 ../mail/mail-ops.c:2017 +#, c-format +msgid "" +"Error saving messages to: %s:\n" +" %s" +msgstr "" +"Gặp lỗi khi lưu thư vào: %s:\n" +" %s" + +#: ../mail/mail-ops.c:2089 ../mail/mail-ops.c:2088 +msgid "Saving attachment" +msgstr "Đang lưu đính kèm" + +#: ../mail/mail-ops.c:2101 ../mail/mail-ops.c:2100 +#, c-format +msgid "" +"Cannot create output file: %s:\n" +" %s" +msgstr "" +"Không thể tạo tập tin xuất: %s:\n" +" %s" + +#: ../mail/mail-ops.c:2111 ../mail/mail-ops.c:2110 +#, c-format +msgid "Could not write data: %s" +msgstr "Không thể ghi dữ liệu : %s" + +#: ../mail/mail-ops.c:2261 ../mail/mail-ops.c:2260 +#, c-format +msgid "Disconnecting from %s" +msgstr "Đang ngắt kết nối từ %s..." + +#: ../mail/mail-ops.c:2261 ../mail/mail-ops.c:2260 +#, c-format +msgid "Reconnecting to %s" +msgstr "Đang tái kết nối tới %s..." + +#: ../mail/mail-ops.c:2377 ../mail/mail-ops.c:2376 +msgid "Checking Service" +msgstr "Đang kiểm tra dịch vụ..." + +#: ../mail/mail-send-recv.c:158 +msgid "Cancelling..." +msgstr "Đang hủy bỏ..." + +#: ../mail/mail-send-recv.c:265 +#, c-format +msgid "Server: %s, Type: %s" +msgstr "" +"Máy phục vụ : %s\n" +"Kiểu : %s" + +#: ../mail/mail-send-recv.c:267 +#, c-format +msgid "Path: %s, Type: %s" +msgstr "" +"Đường dẫn: %s\n" +"Kiểu : %s" + +#: ../mail/mail-send-recv.c:269 tools/interface.c:1876 +#, c-format +msgid "Type: %s" +msgstr "Kiểu : %s" + +#: ../mail/mail-send-recv.c:320 +msgid "Send & Receive Mail" +msgstr "Gởi và Nhận Thư" + +#: ../mail/mail-send-recv.c:327 +msgid "Cancel _All" +msgstr "Thôi _hết" + +#: ../mail/mail-send-recv.c:416 ../gtik/gtik.c:305 +#: ../gweather/gweather-applet.c:545 +msgid "Updating..." +msgstr "Đang cập nhật..." + +#: ../mail/mail-send-recv.c:416 ../mail/mail-send-recv.c:468 +msgid "Waiting..." +msgstr "Đang chờ..." + +#: ../mail/mail-send-recv.c:699 +#: ../camel/providers/groupwise/camel-groupwise-provider.c:51 +#: ../camel/providers/imap4/camel-imap4-provider.c:36 +msgid "Checking for new mail" +msgstr "Đang kiểm tra tìm thư mới..." + +#: ../mail/mail-session.c:207 +#, c-format +msgid "Enter Password for %s" +msgstr "Nhập mật khẩu cho « %s »" + +#: ../mail/mail-session.c:206 ../mail/mail-session.c:209 +#: ../interfaces/common.glade.in.h:3 ../src/FlickrExport.cs:49 +msgid "Enter Password" +msgstr "Nhập mật khẩu" + +#: ../mail/mail-session.c:241 ../mail/mail-session.c:244 +msgid "User canceled operation." +msgstr "Người dùng đã hủy bỏ tác vụ." + +#: ../mail/mail-signature-editor.c:384 ../mail/mail-signature-editor.c:372 +msgid "Edit signature" +msgstr "Sửa đổi chữ ký" + +#: ../mail/mail-signature-editor.c:431 ../mail/mail-signature-editor.c:412 +msgid "Enter a name for this signature." +msgstr "Nhập tên cho chữ ký này." + +#: ../src/gtkfunc.c:269 +msgid "Name:" +msgstr "Tên:" + +#: ../mail/mail-tools.c:120 ../mail/mail-tools.c:116 +#, c-format +msgid "Could not create spool directory `%s': %s" +msgstr "Không thể tạo thư mục ống chỉ « %s »: %s" + +#: ../mail/mail-tools.c:150 ../mail/mail-tools.c:143 +#, c-format +msgid "Trying to movemail a non-mbox source `%s'" +msgstr "Đang cố movemail (di chuyển thư) một nguồn không dạng mbox « %s »" + +#: ../mail/mail-tools.c:256 ../mail/mail-tools.c:242 +#, c-format +msgid "Forwarded message - %s" +msgstr "Thư đã chuyển tiếp - %s" + +#: ../mail/mail-tools.c:258 ../mail/mail-tools.c:244 +msgid "Forwarded message" +msgstr "Thư đã chuyển tiếp" + +#: ../mail/mail-tools.c:298 ../mail/mail-tools.c:284 +#, c-format +msgid "Invalid folder: `%s'" +msgstr "Thư mục không hợp lệ: « %s »" + +#: ../mail/mail-vfolder.c:91 +#, c-format +msgid "Setting up Search Folder: %s" +msgstr "Đang thiết lập thư mục tìm kiếm: %s" + +#: ../mail/mail-vfolder.c:240 +#, c-format +msgid "Updating Search Folders for '%s:%s'" +msgstr "Đang cập nhật các thư mục tìm kiếm cho « %s:%s »..." + +#: ../mail/mail-vfolder.c:247 +#, c-format +msgid "Updating Search Folders for '%s'" +msgstr "Đang cập nhật các thư mục tìm kiếm cho « %s »..." + +#: ../mail/mail-vfolder.c:1050 ../mail/mail-vfolder.c:1046 +msgid "Edit Search Folder" +msgstr "Hiệu chỉnh thư mục tìm kiếm" + +#: ../mail/mail-vfolder.c:1134 ../mail/mail-vfolder.c:1130 +msgid "New Search Folder" +msgstr "Thư mục tìm kiếm mới" + +#: ../mail/mail.error.xml.h:1 +msgid "" +"A folder named "{1}" already exists. Please use a different name." +msgstr "Thư mục tên « {1} » đã có. Hãy sử dụng tên khác." + +#: ../mail/mail.error.xml.h:2 +msgid "" +"A non-empty folder at "{1}" already exists.\n" +"\n" +"You can choose to ignore this folder, overwrite or append its contents, or " +"quit.\n" +msgstr "" +"Một thư mục không rỗng tại « {1} » đã có.\n" +"\n" +"Bạn có thể chọn bỏ qua thư mục này, ghi đè lên nó, phụ thêm nội dung nó, " +"hoặc thoát.\n" + +#: ../mail/mail.error.xml.h:6 +msgid "" +"A read receipt notification has been requested for "{1}". Send " +"the reciept notification to {0}?" +msgstr "" +"Yêu cầu một thông báo đã đọc cho « {1} ». Gởi thông báo đó cho « {0} » không?" + +#: ../mail/mail.error.xml.h:7 +msgid "" +"A signature already exists with the name "{0}". Please specify a " +"different name." +msgstr "Chữ ký tên « {1} » đã có. Hãy gõ tên khác." + +#: ../mail/mail.error.xml.h:8 ../mail/mail.error.xml.h:7 +msgid "" +"Adding a meaningful Subject line to your messages will give your recipients " +"an idea of what your mail is about." +msgstr "" +"Thêm một Chủ đề có nghĩa vào thư bạn sẽ cho người nhận ý kiến về nội dung. " +"Nhiều người sẽ bỏ qua thư không có Chủ đề (vì thường là thư rác)." + +#: ../mail/mail.error.xml.h:9 ../mail/mail.error.xml.h:8 +msgid "Are you sure you want to delete this account and all its proxies?" +msgstr "Bạn có chắc muốn xoá bỏ tài khoản này và các ủy nhiệm của nó không?" + +#: ../mail/mail.error.xml.h:10 ../mail/mail.error.xml.h:9 +msgid "Are you sure you want to delete this account?" +msgstr "Bạn có muốn xoá bỏ tài khoản này không?" + +#: ../mail/mail.error.xml.h:11 ../mail/mail.error.xml.h:10 +msgid "Are you sure you want to open {0} messages at once?" +msgstr "Bạn có chắc muốn mở cả {0} thư cùng lúc không?" + +#: ../mail/mail.error.xml.h:12 ../mail/mail.error.xml.h:11 +msgid "" +"Are you sure you want to permanently remove all the deleted messages in all " +"folders?" +msgstr "" +"Bạn có chắc muốn gỡ bỏ hoàn toàn mọi thư đã xoá bỏ trong mọi thư mục không?" + +#: ../mail/mail.error.xml.h:13 ../mail/mail.error.xml.h:12 +msgid "" +"Are you sure you want to permanently remove all the deleted messages in " +"folder "{0}"?" +msgstr "" +"Bạn có chắc muốn xoá bỏ hoàn toàn mọi thư đã xoá bỏ trong thư mục « {0} » " +"không?" + +#: ../mail/mail.error.xml.h:14 ../mail/mail.error.xml.h:13 +msgid "Are you sure you want to send a message in HTML format?" +msgstr "Bạn có chắc muốn gởi thư theo dạng HTML không?" + +#: ../mail/mail.error.xml.h:15 ../mail/mail.error.xml.h:14 +msgid "Are you sure you want to send a message with only BCC recipients?" +msgstr "" +"Bạn có chắc muốn gởi thư chỉ có người nhận Bí mật Chép Cho (BCC) không?" + +#: ../mail/mail.error.xml.h:16 ../mail/mail.error.xml.h:15 +msgid "Are you sure you want to send a message without a subject?" +msgstr "Bạn có chắc muốn gởi thư không có chủ đề không? (Không đệ nghị)" + +#: ../mail/mail.error.xml.h:17 ../mail/mail.error.xml.h:16 +msgid "Because "{0}"." +msgstr "Vì « {0} »." + +#: ../mail/mail.error.xml.h:19 ../mail/mail.error.xml.h:18 +msgid "Because "{2}"." +msgstr "Vì « {2} »." + +#: ../mail/mail.error.xml.h:20 +msgid "Blank Signature" +msgstr "Chữ ký rỗng" + +#: ../mail/mail.error.xml.h:21 ../mail/mail.error.xml.h:19 +msgid "Cannot add Search Folder "{0}"." +msgstr "Không thể thêm thư mục tìm kiếm « {0}»." + +#: ../mail/mail.error.xml.h:22 ../mail/mail.error.xml.h:20 +msgid "Cannot copy folder "{0}" to "{1}"." +msgstr "Không thể sao chép thư mục « {0} » vào « {1} »." + +#: ../mail/mail.error.xml.h:23 ../mail/mail.error.xml.h:21 +msgid "Cannot create folder "{0}"." +msgstr "Không thể tạo thư mục « {0} »." + +#: ../mail/mail.error.xml.h:24 +msgid "Cannot create temporary save directory." +msgstr "Không thể tạo thư mục lưu tạm." + +#: ../mail/mail.error.xml.h:25 +msgid "Cannot create the save directory, because "{1}"" +msgstr "Không thể tạo thư mục lưu, vì « {1} »." + +#: ../mail/mail.error.xml.h:26 ../mail/mail.error.xml.h:24 +msgid "Cannot delete folder "{0}"." +msgstr "Không thể xoá bỏ thư mục « {0} »." + +#: ../mail/mail.error.xml.h:27 ../mail/mail.error.xml.h:25 +msgid "Cannot delete system folder "{0}"." +msgstr "Không thể xoá bỏ thư mục hệ thống « {0} »." + +#: ../mail/mail.error.xml.h:28 ../mail/mail.error.xml.h:26 +msgid "Cannot edit Search Folder "{0}" as it does not exist." +msgstr "Không thể hiệu chỉnh thư mục tìm kiếm « {0} » vì nó không tồn tại." + +#: ../mail/mail.error.xml.h:29 ../mail/mail.error.xml.h:27 +msgid "Cannot move folder "{0}" to "{1}"." +msgstr "Không thể di chuyển thư mục « {0} » đến « {1} »." + +#: ../mail/mail.error.xml.h:30 ../mail/mail.error.xml.h:28 +msgid "Cannot open source "{1}"" +msgstr "Không thể mở nguồn « {1} »." + +#: ../mail/mail.error.xml.h:31 ../mail/mail.error.xml.h:29 +msgid "Cannot open source "{2}"." +msgstr "Không thể mở nguồn « {2} »." + +#: ../mail/mail.error.xml.h:32 ../mail/mail.error.xml.h:30 +msgid "Cannot open target "{2}"." +msgstr "Không thể mở đích « {2} »." + +#: ../mail/mail.error.xml.h:33 ../mail/mail.error.xml.h:31 +msgid "" +"Cannot read the license file "{0}", due to an installation " +"problem. You will not be able to use this provider until you can accept its " +"license." +msgstr "" +"Không thể đọc tập tin quyền « {0} » vì gặp lỗi cài đặt. Bạn sẽ không thể sử " +"dụng nhà cung cấp này cho đến khi có thể chấp nhận quyền của nó." + +#: ../mail/mail.error.xml.h:34 ../mail/mail.error.xml.h:32 +msgid "Cannot rename "{0}" to "{1}"." +msgstr "Không thể thay đổi tên thư mục « {0} » sang « {1} »." + +#: ../mail/mail.error.xml.h:35 ../mail/mail.error.xml.h:33 +msgid "Cannot rename or move system folder "{0}"." +msgstr "Không thể thay đổi tên hoặc di chuyển thư mục hệ thống « {0} »." + +#: ../mail/mail.error.xml.h:36 ../mail/mail.error.xml.h:34 +msgid "Cannot save changes to account." +msgstr "Không thể lưu các thay đổi trong tài khoản." + +#: ../mail/mail.error.xml.h:37 +msgid "Cannot save to directory "{0}"." +msgstr "Không thể lưu vào thư mục « {0} »." + +#: ../mail/mail.error.xml.h:38 ../mail/mail.error.xml.h:36 +msgid "Cannot save to file "{0}"." +msgstr "Không thể lưu vào tập tin « {0} »." + +#: ../mail/mail.error.xml.h:39 ../mail/mail.error.xml.h:37 +msgid "Cannot set signature script "{0}"." +msgstr "Không thể lập tập lệnh chữ ký « {0} »." + +#: ../mail/mail.error.xml.h:40 ../mail/mail.error.xml.h:38 +msgid "" +"Check to make sure your password is spelled correctly. Remember that many " +"passwords are case sensitive; your caps lock might be on." +msgstr "" +"Hãy kiểm tra xem đã gõ mật khẩu cho đúng. Nhiều mật khẩu phân biệt chữ hoa, " +"chữ thường; phím CapsLock (chữ hoa luôn) phải tắt." + +#: ../mail/mail.error.xml.h:41 ../mail/mail.error.xml.h:39 +msgid "Could not save signature file." +msgstr "Không thể lưu tập tin chữ ký." + +#: ../mail/mail.error.xml.h:42 ../mail/mail.error.xml.h:40 +msgid "Delete "{0}"?" +msgstr "Xoá bỏ « {0} » không?" + +#: ../mail/mail.error.xml.h:43 ../mail/mail.error.xml.h:41 +msgid "Delete account?" +msgstr "Xoá bỏ tài khoản không?" + +#: ../mail/mail.error.xml.h:44 +msgid "Discard changes?" +msgstr "Hủy các thay đổi không?" + +#: ../mail/mail.error.xml.h:45 ../mail/mail.error.xml.h:43 +msgid "Do you want the operation to be performed in the subfolders?" +msgstr "Bạn có muốn thực hiện thao tác đó xuống những thư mục con không?" + +#: ../mail/mail.error.xml.h:46 ../mail/mail.error.xml.h:44 +msgid "Do you wish to save your changes?" +msgstr "Bạn có muốn lưu các thay đổi không?" + +#: ../mail/mail.error.xml.h:47 ../mail/mail.error.xml.h:45 +msgid "Don't delete" +msgstr "Không xoá bỏ" + +#: ../mail/mail.error.xml.h:48 ../mail/mail.error.xml.h:46 +msgid "Enter password." +msgstr "Hãy gõ mật khẩu." + +#: ../mail/mail.error.xml.h:49 ../mail/mail.error.xml.h:47 +msgid "Error loading filter definitions." +msgstr "Gặp lỗi khi tải lời định nghĩa bộ lọc." + +#: ../mail/mail.error.xml.h:50 ../mail/mail.error.xml.h:48 +msgid "Error while performing operation." +msgstr "Gặp lỗi khi thực hiện thao tác." + +#: ../mail/mail.error.xml.h:51 ../mail/mail.error.xml.h:49 +msgid "Error while {0}." +msgstr "Gặp lỗi khi « {0} »." + +#: ../mail/mail.error.xml.h:52 ../mail/mail.error.xml.h:50 +msgid "File exists but cannot overwrite it." +msgstr "Tập tin đã tồn tại nhưng không thể ghi đè lên nó." + +#: ../mail/mail.error.xml.h:53 ../mail/mail.error.xml.h:51 +msgid "File exists but is not a regular file." +msgstr "Tập tin tồn tại nhưng không phải là tập tin bình thường." + +#: ../mail/mail.error.xml.h:54 ../mail/mail.error.xml.h:52 +msgid "If you continue, you will not be able to recover these messages." +msgstr "Nếu bạn tiếp tục, bạn sẽ không thể phục hồi những thư này." + +#: ../mail/mail.error.xml.h:55 ../mail/mail.error.xml.h:53 +msgid "" +"If you delete the folder, all of its contents and its subfolders contents " +"will be deleted permanently." +msgstr "" +"Nếu bạn xoá bỏ thư mục đó thì sẽ xoá bỏ hoàn toàn mọi nội dung và thư mục " +"con của nó." + +#: ../mail/mail.error.xml.h:56 ../mail/mail.error.xml.h:54 +msgid "" +"If you proceed, the account information and\n" +"all proxy information will be deleted permanently." +msgstr "" +"Nếu bạn tiếp tục, sẽ xoá bỏ hoàn toàn thông tin tài khoản đó và các thông " +"tin ủy nhiệm của nó." + +#: ../mail/mail.error.xml.h:58 ../mail/mail.error.xml.h:56 +msgid "If you proceed, the account information will be deleted permanently." +msgstr "Nếu bạn tiếp tục, sẽ xoá bỏ hoàn toàn thông tin tài khoản đó." + +#: ../mail/mail.error.xml.h:59 ../mail/mail.error.xml.h:57 +msgid "" +"If you quit, these messages will not be sent until Evolution is started " +"again." +msgstr "" +"Nếu bạn thoát thì sẽ không gởi những thư này tới khi khởi chạy lại trình " +"Evolution." + +#: ../mail/mail.error.xml.h:61 ../mail/mail.error.xml.h:59 +msgid "Invalid authentication" +msgstr "Xác thực không hợp lệ" + +#: ../mail/mail.error.xml.h:62 ../mail/mail.error.xml.h:60 +msgid "Mail filters automatically updated." +msgstr "Các bộ lọc thư đã được cập nhật tự động." + +#: ../mail/mail.error.xml.h:63 ../mail/mail.error.xml.h:61 +msgid "" +"Many email systems add an Apparently-To header to messages that only have " +"BCC recipients. This header, if added, will list all of your recipients to " +"your message anyway. To avoid this, you should add at least one To: or CC: " +"recipient." +msgstr "" +"Nhiều hệ thống thư điện tử thêm một dòng đầu « Hình như Cho » (Apparently-" +"To) vào mọi thư chỉ có người nhận BCC (Bí mật Chép Cho). Nếu thêm dòng đầu " +"đó, nó sẽ liệt kê mọi người nhận trong thư của bạn. Để tránh người gởi thư " +"Rác ăn cấp các địa chỉ trong danh sách đó, bạn hãy thêm ít nhất một người " +"nhận Cho (To) hay Chép Cho (Cc), v.d. địa chỉ mình." + +#: ../mail/mail.error.xml.h:64 ../mail/mail.error.xml.h:62 +msgid "Mark all messages as read" +msgstr "Đánh dấu mọi thư Đã đọc" + +#: ../mail/mail.error.xml.h:65 ../mail/mail.error.xml.h:63 +msgid "Mark all messages as read in the selected folder" +msgstr "Đánh dấu mọi thư Đã đọc trong thư mục đã chọn" + +#: ../mail/mail.error.xml.h:66 +msgid "Missing folder." +msgstr "Thiếu thư mục." + +#: ../mail/mail.error.xml.h:68 ../mail/mail.error.xml.h:66 +msgid "No sources selected." +msgstr "Chưa chọn nguồn." + +#: ../mail/mail.error.xml.h:69 ../mail/mail.error.xml.h:67 +msgid "Opening too many messages at once may take a long time." +msgstr "Mở quá nhiều thư cùng lúc có lẽ sẽ mất lâu." + +#: ../mail/mail.error.xml.h:70 ../mail/mail.error.xml.h:68 +msgid "Please check your account settings and try again." +msgstr "Hãy kiểm tra xem thiết lập tài khoản rồi thử lại." + +#: ../mail/mail.error.xml.h:71 +msgid "Please enable the account or send using another account." +msgstr "Hãy bật tài khoản này hoặc gởi bằng tài khoản khác." + +#: ../mail/mail.error.xml.h:72 ../mail/mail.error.xml.h:69 +msgid "" +"Please enter a valid email address in the To: field. You can search for " +"email addresses by clicking on the To: button next to the entry box." +msgstr "" +"Hãy nhập một địa chỉ thư điện tử hợp lệ vào trường Cho: (To). Có thể tìm " +"kiếm địa chỉ thư bằng cách nhắp vào nút Cho: (To) ở cạnh hộp nhập." + +#: ../mail/mail.error.xml.h:73 ../mail/mail.error.xml.h:70 +msgid "" +"Please make sure the following recipients are willing and able to receive " +"HTML email:\n" +"{0}\n" +"Send anyway?" +msgstr "" +"Hãy đảm bảo rằng những người nhận sau có thể và cũng muốn nhận thư dạng " +"HTML:\n" +"{0}\n" +"Gởi bất chấp không?" + +#: ../mail/mail.error.xml.h:76 +msgid "Please provide an unique name to identify this signature." +msgstr "Hãy cung cấp tên duy nhất để nhận diện chữ ký này." + +#: ../mail/mail.error.xml.h:77 ../mail/mail.error.xml.h:73 +msgid "Please wait." +msgstr "Vui lòng chờ" + +#: ../mail/mail.error.xml.h:78 ../mail/mail.error.xml.h:74 +msgid "Problem migrating old mail folder "{0}"." +msgstr "Gặp lỗi khi chuyển đổi thư mục thư cũ « {0} »." + +#: ../mail/mail.error.xml.h:79 ../mail/mail.error.xml.h:75 +msgid "Querying server" +msgstr "Đang truy vấn máy phục vụ..." + +#: ../mail/mail.error.xml.h:80 ../mail/mail.error.xml.h:76 +msgid "Querying server for a list of supported authentication mechanisms." +msgstr "" +"Đang truy vấn máy phục vụ có danh sách các cơ chế xác thực được hỗ trợ." + +#: ../mail/mail.error.xml.h:81 ../mail/mail.error.xml.h:77 +msgid "Read receipt requested." +msgstr "Thông báo đã đọc đã được yêu cầu." + +#: ../mail/mail.error.xml.h:82 ../mail/mail.error.xml.h:78 +msgid "Really delete folder "{0}" and all of its subfolders?" +msgstr "" +"Bạn thật sự muốn xoá bỏ thư mục « {0} » và mọi thư mục con của nó không?" + +#: ../mail/mail.error.xml.h:83 ../mail/mail.error.xml.h:79 +msgid "Search Folders automatically updated." +msgstr "Các thư mục tìm kiếm đã được cập nhật tự động." + +#: ../mail/mail.error.xml.h:84 ../mail/mail.error.xml.h:80 +msgid "Send Receipt" +msgstr "Gởi thông báo đã đọc" + +#: ../mail/mail.error.xml.h:85 +msgid "Signature Already Exists" +msgstr "Chữ ký đã có" + +#: ../mail/mail.error.xml.h:86 ../mail/mail.error.xml.h:81 +msgid "" +"System folders are required for Ximian Evolution to function correctly and " +"cannot be renamed, moved, or deleted." +msgstr "" +"Các thư mục hệ thống có cần thiết để trình Ximian Evolution hoạt động cho " +"đúng nên không thể thay đổi tên, di chuyển hay xoá bỏ chúng." + +#: ../mail/mail.error.xml.h:87 ../mail/mail.error.xml.h:82 +msgid "" +"The contact list you are sending to is configured to hide list recipients.\n" +"\n" +"Many email systems add an Apparently-To header to messages that only have " +"BCC recipients. This header, if added, will list all of your recipients in " +"your message. To avoid this, you should add at least one To: or CC: " +"recipient. " +msgstr "" +"Bạn đang gởi cho một danh sách liên lạc có cấu hình ẩn mọi người nhận có " +"trong danh sách đó.\n" +"\n" +"Nhiều hệ thống thư điện tử thêm một dòng đầu « Hình như Cho » (Apparently-" +"To) vào mọi thư chỉ có người nhận BCC (Bí mật Chép Cho). Nếu thêm dòng đầu " +"đó, nó sẽ liệt kê mọi người nhận trong thư của bạn. Để tránh người gởi thư " +"Rác ăn cấp các địa chỉ trong danh sách đó, bạn hãy thêm ít nhất một người " +"nhận Cho (To) hay Chép Cho (Cc), v.d. địa chỉ mình." + +#: ../mail/mail.error.xml.h:90 ../mail/mail.error.xml.h:85 +msgid "" +"The following Search Folder(s):\n" +"{0}\n" +"Used the now removed folder:\n" +" « {1} »\n" +"And have been updated." +msgstr "" +"Những thư mục tìm kiếm theo đây:\n" +"{0}\n" +"đã dùng thư mục mới bị gỡ bỏ :\n" +" « {1} »\n" +"và đã được cập nhật." + +#: ../mail/mail.error.xml.h:95 ../mail/mail.error.xml.h:90 +msgid "" +"The following filter rule(s):\n" +"{0}\n" +"Used the now removed folder:\n" +" "{1}"\n" +"And have been updated." +msgstr "" +"Những quy tắc lọc theo đây:\n" +"{0}\n" +"đã dùng thư mục mới bị gỡ bỏ :\n" +" « {1} »\n" +"và đã được cập nhật." + +#: ../mail/mail.error.xml.h:100 ../mail/mail.error.xml.h:95 +msgid "" +"The message was sent via the "sendmail" external application. " +"Sendmail reports the following error: status 67: mail not sent.\n" +"The message is stored in the Outbox folder. Check the message for errors " +"and resend." +msgstr "" +"Đã cố gởi thư đó thông qua ứng dụng ở ngoại sendmail. Trình sendmail thông " +"báo lỗi này:\n" +"status 67: mail not sent (trạng thái 67, chưa gởi thư)\n" +"Đã cất giữ thư đó vào thư mục Hộp Đi (Outbox). Hãy kiểm tra xem lỗi trong " +"thư đó và gởi lại." + +#: ../mail/mail.error.xml.h:102 ../mail/mail.error.xml.h:97 +msgid "The script file must exist and be executable." +msgstr "Tập tin tập lệnh phải tồn tại và có chạy được." + +#: ../mail/mail.error.xml.h:103 ../mail/mail.error.xml.h:98 +msgid "" +"This folder may have been added implicitly,\n" +"go to the Search Folder editor to add it explicitly, if required." +msgstr "" +"Có lẽ đã thêm thư mục này một cách ngầm; hãy dùng bộ hiệu chỉnh thư mục tìm " +"kiếm để thêm nó một cách dứt khoát, nếu cần thiết." + +#: ../mail/mail.error.xml.h:105 +msgid "" +"This message cannot be sent because the account you chose to send with is " +"not enabled" +msgstr "" +"Không thể gởi thư này vì bạn đang gởi nó bằng một tài khoản chưa được bật" + +#: ../mail/mail.error.xml.h:106 ../mail/mail.error.xml.h:100 +msgid "" +"This message cannot be sent because you have not specified any Recipients" +msgstr "Không gởi được thư này vì bạn chưa ghi rõ người nhận nào." + +#: ../mail/mail.error.xml.h:107 ../mail/mail.error.xml.h:101 +msgid "" +"This server does not support this type of authentication and may not support " +"authentication at all." +msgstr "" +"Máy phục vụ này không hỗ trợ loại xác thực này và có lẽ hoàn toàn không hỗ " +"trợ xác thực nào." + +#: ../mail/mail.error.xml.h:108 ../mail/mail.error.xml.h:102 +msgid "This signature has been changed, but has not been saved." +msgstr "Chữ ký này đã thay đổi, nhưng vẫn chưa được lưu." + +#: ../mail/mail.error.xml.h:109 ../mail/mail.error.xml.h:103 +msgid "Unable to connect to the GroupWise server." +msgstr "Không kết nối tới máy phục vụ Groupwise được." + +#: ../mail/mail.error.xml.h:110 ../mail/mail.error.xml.h:104 +msgid "" +"Unable to open the drafts folder for this account. Use the system drafts " +"folder instead?" +msgstr "" +"Không thể mở thư mục Nháp cho tài khoản này. Dùng thư mục Nháp của hệ thống " +"chứ?" + +#: ../mail/mail.error.xml.h:111 ../mail/mail.error.xml.h:105 +msgid "Unable to read license file." +msgstr "Không đoc được tập tin quyền." + +#: ../mail/mail.error.xml.h:112 ../mail/mail.error.xml.h:106 +#: ../glade/straw.glade.h:64 +msgid "Use _Default" +msgstr "Dùng _mặc định" + +#: ../mail/mail.error.xml.h:113 ../mail/mail.error.xml.h:107 +msgid "Use default drafts folder?" +msgstr "Dùng thư mục nháp mặc định chứ?" + +#: ../mail/mail.error.xml.h:114 ../mail/mail.error.xml.h:108 +msgid "You have not filled in all of the required information." +msgstr "Bạn chưa điền đủ các thông tin yêu cầu." + +#: ../mail/mail.error.xml.h:115 ../mail/mail.error.xml.h:109 +msgid "You have unsent messages, do you wish to quit anyway?" +msgstr "Bạn có vài thư chưa gởi, bạn vẫn muốn thoát sao?" + +#: ../mail/mail.error.xml.h:116 ../mail/mail.error.xml.h:110 +msgid "You may not create two accounts with the same name." +msgstr "Không cho phép bạn tạo hai tài khoản trùng tên." + +#: ../mail/mail.error.xml.h:117 ../mail/mail.error.xml.h:111 +msgid "You must name this Search Folder." +msgstr "Bạn phải đặt tên cho thư mục tìm kiếm này." + +#: ../mail/mail.error.xml.h:118 +msgid "You must specify a folder." +msgstr "Bạn phải xác định thư mục." + +#: ../mail/mail.error.xml.h:119 ../mail/mail.error.xml.h:113 +msgid "" +"You must specify at least one folder as a source.\n" +"Either by selecting the folders individually, and/or by selecting all local " +"folders, all remote folders, or both." +msgstr "" +"Bạn phải ghi rõ ít nhất một thư mục là nguồn,\n" +"hoặc bằng cách chọn mỗi thư mục từng một cái,\n" +"hoặc/và bằng cách chọn mọi thư mục địa phương,\n" +"mọi thư mục ở xa, hoặc cả hai." + +#: ../mail/mail.error.xml.h:121 ../mail/mail.error.xml.h:115 +msgid "Your login to your server "{0}" as "{0}" failed." +msgstr "" +"Việc đăng nhập của bạn vào máy phục vụ « {0} » với tư cách « {0} » bị lỗi." + +#: ../mail/mail.error.xml.h:122 ../mail/mail.error.xml.h:116 +msgid "Your message with the subject "{0}" was not delivered." +msgstr "Chưa phát thư của bạn có chủ đề « {0} »." + +#: ../mail/mail.error.xml.h:123 ../mail/mail.error.xml.h:117 +msgid "_Append" +msgstr "Phụ th_êm" + +#: ../mail/mail.error.xml.h:124 ../mail/mail.error.xml.h:118 +msgid "_Discard changes" +msgstr "_Hủy các thay đổi" + +#: ../mail/mail.error.xml.h:126 ../mail/mail.error.xml.h:120 +msgid "_Expunge" +msgstr "_Xoá hẳn" + +#: ../mail/mail.error.xml.h:127 ../mail/mail.error.xml.h:121 +msgid "_Open Messages" +msgstr "_Mở các thư" + +#: ../mail/message-list.c:1004 ../mail/message-list.c:996 +msgid "Unseen" +msgstr "Chưa xem" + +#: ../mail/message-list.c:1005 ../mail/message-list.c:997 +msgid "Seen" +msgstr "Đã xem" + +#: ../mail/message-list.c:1006 ../mail/message-list.c:998 +msgid "Answered" +msgstr "Đã trả lời" + +#: ../mail/message-list.c:1007 ../mail/message-list.c:999 +msgid "Multiple Unseen Messages" +msgstr "Nhiều thư chưa xem" + +#: ../mail/message-list.c:1008 ../mail/message-list.c:1000 +msgid "Multiple Messages" +msgstr "Nhiều thư" + +#: ../mail/message-list.c:1012 ../mail/message-list.c:1004 +msgid "Lowest" +msgstr "Thấp nhất" + +#: ../mail/message-list.c:1013 ../mail/message-list.c:1005 +msgid "Lower" +msgstr "Thấp hơn" + +#: ../mail/message-list.c:1017 ../mail/message-list.c:1009 +msgid "Higher" +msgstr "Cao hơn" + +#: ../mail/message-list.c:1018 ../mail/message-list.c:1010 +msgid "Highest" +msgstr "Cao nhất" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 12-hour format, without seconds. +#: ../src/gnome-keyring-manager-util.c:219 ../storage/sunone-itip-view.c:152 +msgid "Today %l:%M %p" +msgstr "Hôm nay %l:%M %p" + +#: ../src/gnome-keyring-manager-util.c:231 +msgid "Yesterday %l:%M %p" +msgstr "Hôm qua %l:%M %p" + +#: ../src/gnome-keyring-manager-util.c:247 +msgid "%a %l:%M %p" +msgstr "%a %l:%M %p" + +#: ../mail/message-list.c:1382 ../widgets/table/e-cell-date.c:102 +#: ../src/gnome-keyring-manager-util.c:258 +msgid "%b %d %l:%M %p" +msgstr "%d %b %l:%M %p" + +#: ../mail/message-list.c:1384 ../widgets/table/e-cell-date.c:104 +#: ../src/gnome-keyring-manager-util.c:262 +msgid "%b %d %Y" +msgstr "%d %b %Y" + +#: ../mail/message-list.c:2055 ../mail/message-list.c:2042 +msgid "Message List" +msgstr "Danh sách thư" + +#: ../mail/message-list.c:3400 ../mail/message-list.c:3387 +msgid "Generating message list" +msgstr "Đang tạo danh sách thư" + +#: ../mail/message-list.etspec.h:3 +msgid "Due By" +msgstr "Đến hạn" + +#: ../mail/message-list.etspec.h:4 +msgid "Flag Status" +msgstr "Trạng thái cờ" + +#: ../mail/message-list.etspec.h:6 +msgid "Follow Up Flag" +msgstr "Cờ theo dõi tiếp" + +#: ../mail/message-list.etspec.h:8 +msgid "Original Location" +msgstr "Địa điểm gốc" + +#: ../mail/message-list.etspec.h:9 ../src/statusview.c:955 +#: ../Tiles/TileMailMessage.cs:116 ../src/history.c:111 ../src/history.c:154 +msgid "Received" +msgstr "Đã nhận" + +#: ../mail/message-tag-followup.c:74 ../objects/UML/message.c:135 +msgid "Call" +msgstr "Gọi" + +#: ../mail/message-tag-followup.c:76 ../mail/message-tag-followup.c:75 +msgid "Do Not Forward" +msgstr "Không chuyển tiếp" + +#: ../mail/message-tag-followup.c:77 ../mail/message-tag-followup.c:76 +msgid "Follow-Up" +msgstr "Theo dõi tiếp" + +#: ../mail/message-tag-followup.c:78 ../mail/message-tag-followup.c:77 +msgid "For Your Information" +msgstr "Cho bạn biết tin tức này" + +#: ../mail/message-tag-followup.c:80 ../mail/message-tag-followup.c:79 +msgid "No Response Necessary" +msgstr "Không cần thiết trả lời" + +#: ../mail/message-tag-followup.c:83 ../ui/evolution-mail-message.xml.h:82 +#: ../mail/message-tag-followup.c:82 ../ui/evolution-mail-message.xml.h:80 +msgid "Reply to All" +msgstr "Trả lời tất cả" + +#: ../mail/message-tag-followup.c:84 src/ui/gtk/learningpref.c:236 +#: ../mail/message-tag-followup.c:83 +msgid "Review" +msgstr "Xem lại" + +#: ../mail/searchtypes.xml.h:1 +msgid "Body contains" +msgstr "Phần thân chứa" + +#: ../mail/searchtypes.xml.h:2 +msgid "Message contains" +msgstr "Thư chứa" + +#: ../mail/searchtypes.xml.h:3 +msgid "Recipients contain" +msgstr "Người nhận chứa" + +#: ../mail/searchtypes.xml.h:4 +msgid "Sender contains" +msgstr "Người gởi chứa" + +#: ../mail/searchtypes.xml.h:5 +msgid "Subject contains" +msgstr "Chủ đề chứa" + +#: ../mail/searchtypes.xml.h:6 +msgid "Subject or Sender contains" +msgstr "Chủ đề hay người gởi chứa" + +#: ../plugins/audio-inline/org-gnome-audio-inline.eplug.xml.h:1 +msgid "" +"A formatter plugin which displays audio attachments inline and allows you to " +"play them directly from evolution." +msgstr "" +"Một trình cầm phít định dạng mà hiển thị đính kèm âm thanh trong thư, và cho " +"phép bạn phát chúng một cách trực tiếp từ trình Evolution." + +#: ../plugins/audio-inline/org-gnome-audio-inline.eplug.xml.h:2 +msgid "Audio inline plugin" +msgstr "Trình cầm phít trực tiếp âm thanh" + +#: ../plugins/backup-restore/backup-restore.c:51 +msgid "Select name of Evolution archive" +msgstr "Chọn tên của kho Evolution" + +#: ../plugins/backup-restore/backup-restore.c:61 +msgid "_Restart Evolution after backup" +msgstr "_Khởi chạy lại Evolution sau khi lưu trữ" + +#: ../plugins/backup-restore/backup-restore.c:89 +msgid "Select Evolution archive to restore" +msgstr "Chọn kho Evolution cần phục hồi" + +#: ../plugins/backup-restore/backup-restore.c:97 +msgid "_Restart Evolution after restore" +msgstr "_Khởi chạy lại Evolution sau khi phục hồi" + +#: ../plugins/backup-restore/backup.c:109 +msgid "Backup Evolution directory" +msgstr "Thư mục lưu trữ Evolution" + +#: ../plugins/backup-restore/backup.c:111 +msgid "Restore Evolution directory" +msgstr "Thư mục phục hồi Evolution" + +#: ../plugins/backup-restore/backup.c:113 +msgid "Check Evolution archive" +msgstr "Kiểm tra kho Evolution" + +#: ../plugins/backup-restore/backup.c:115 +msgid "Restart Evolution" +msgstr "Khởi chạy lại Evolution" + +#: ../plugins/backup-restore/org-gnome-backup-restore.eplug.xml.h:1 +msgid "A plugin for backing up and restore Evolution data and settings." +msgstr "" +"Bộ cầm phít để lưu trữ và phục hồi dữ liệu và thiết lập của trình Evolution." + +#: ../plugins/backup-restore/org-gnome-backup-restore.eplug.xml.h:2 +msgid "Backup and restore plugin" +msgstr "Bộ cầm phít lưu trữ và phục hồi" + +#: ../plugins/backup-restore/org-gnome-backup-restore.xml.h:1 +msgid "Backup Settings..." +msgstr "Thiết lập lưu trữ..." + +#: ../plugins/backup-restore/org-gnome-backup-restore.xml.h:2 +msgid "Backup and restore Evolution data and settings" +msgstr "Lưu trữ và phục hồi các dữ liệu và thiết lập đều của trình Evolution" + +#: ../plugins/backup-restore/org-gnome-backup-restore.xml.h:3 +msgid "Restore Settings..." +msgstr "Phục hồi thiết lập..." + +#: ../plugins/bbdb/bbdb.c:410 ../plugins/bbdb/bbdb.c:404 +msgid "Automatic Contacts" +msgstr "Liên lạc tự động" + +#: ../plugins/bbdb/bbdb.c:419 ../plugins/bbdb/bbdb.c:413 +msgid "Automatic Contacts" +msgstr "Liên lạc tự động" + +#. Enable BBDB checkbox +#: ../plugins/bbdb/bbdb.c:432 ../plugins/bbdb/bbdb.c:426 +msgid "" +"_Automatically create entries in the addressbook when responding to mail" +msgstr "_Tự động tạo mục nhập trong sổ địa chỉ khi trả lời thư" + +#: ../plugins/bbdb/bbdb.c:450 ../plugins/bbdb/bbdb.c:444 +msgid "Instant Messaging Contacts" +msgstr "Liên lạc tin nhắn tức khắc" + +#. Enable Gaim Checkbox +#: ../plugins/bbdb/bbdb.c:463 ../plugins/bbdb/bbdb.c:457 +msgid "" +"Periodically synchronize contact information and images from my _instant " +"messenger" +msgstr "" +"Đồng bộ hóa theo định kỷ các thông tin liên lạc và ảnh đều từ trình tin nhắn " +"tức khắc của tôi" + +#. Synchronize now button. +#: ../plugins/bbdb/bbdb.c:470 ../plugins/bbdb/bbdb.c:464 +msgid "Synchronize with _buddy list now" +msgstr "Đồng bộ hóa với danh sách người _bạn ngay bây giờ" + +#: ../plugins/bbdb/org-gnome-evolution-bbdb.eplug.xml.h:1 +msgid "Automatic contacts" +msgstr "Liên lạc tự động" + +#: ../plugins/bbdb/org-gnome-evolution-bbdb.eplug.xml.h:2 +msgid "" +"Automatically fills your addressbook with names and email addresses as you " +"reply to mails. Also fills in IM contact information from your buddy lists." +msgstr "" +"Tự động chèn vào sổ địa chỉ các tên và địa chỉ thư đều khi bạn trả lời thư. " +"Cũng chèn thông tin về liên lặc tin nhắn tức khác từ các danh sách người bạn " +"của bạn." + +# Name: don't translate / Tên: đừng dịch +#: ../plugins/bbdb/org-gnome-evolution-bbdb.eplug.xml.h:3 +msgid "BBDB" +msgstr "BBDB" + +#: ../plugins/calendar-file/org-gnome-calendar-file.eplug.xml.h:1 +msgid "Local Calendars" +msgstr "Lịch địa phương" + +#: ../plugins/calendar-file/org-gnome-calendar-file.eplug.xml.h:2 +msgid "Provides core functionality for local calendars." +msgstr "Cung cấp chức năng lõi cho lịch địa phương." + +#: ../plugins/calendar-http/org-gnome-calendar-http.eplug.xml.h:1 +msgid "HTTP Calendars" +msgstr "Lịch HTTP" + +#: ../plugins/calendar-http/org-gnome-calendar-http.eplug.xml.h:2 +msgid "Provides core functionality for webcal and http calendars." +msgstr "Cung cấp chức năng lõi cho lịch webcal và HTTP." + +#: ../calendar/backends/weather/e-cal-backend-weather.c:239 +msgid "Weather: Cloudy" +msgstr "Thời tiết: đầy mây" + +#: ../calendar/backends/weather/e-cal-backend-weather.c:248 +msgid "Weather: Fog" +msgstr "Thời tiết: sương mù" + +#: ../plugins/calendar-weather/calendar-weather.c:62 +#: ../calendar/backends/weather/e-cal-backend-weather.c:231 +msgid "Weather: Partly Cloudy" +msgstr "Thời tiết: phần mây" + +#: ../calendar/backends/weather/e-cal-backend-weather.c:252 +msgid "Weather: Rain" +msgstr "Thời tiết: mưa" + +#: ../calendar/backends/weather/e-cal-backend-weather.c:249 +msgid "Weather: Snow" +msgstr "Thời tiết: tuyết" + +#: ../calendar/backends/weather/e-cal-backend-weather.c:245 +msgid "Weather: Sunny" +msgstr "Thời tiết: trời có nắng" + +#: ../plugins/calendar-weather/calendar-weather.c:66 +#: ../calendar/backends/weather/e-cal-backend-weather.c:233 +msgid "Weather: Thunderstorms" +msgstr "Thời tiết: bão kèm sấm" + +#: ../plugins/calendar-weather/calendar-weather.c:267 +msgid "Select a location" +msgstr "Chọn địa điểm" + +#: ../plugins/calendar-weather/calendar-weather.c:654 +msgid "_Units:" +msgstr "_Đơn vị" + +#: ../plugins/calendar-weather/calendar-weather.c:661 +msgid "Metric (Celsius, cm, etc)" +msgstr "Mét (chia trăm độ, cm v.v.)" + +#: ../plugins/calendar-weather/calendar-weather.c:662 +msgid "Imperial (Fahrenheit, inches, etc)" +msgstr "Mỹ (ái đo nhiệt Fa-ren-hét, insơ v.v.)" + +#: ../plugins/calendar-weather/org-gnome-calendar-weather.eplug.xml.h:1 +msgid "Provides core functionality for weather calendars." +msgstr "Cung cấp chức năng lõi cho lịch thời tiết." + +#: ../plugins/calendar-weather/org-gnome-calendar-weather.eplug.xml.h:2 +msgid "Weather Calendars" +msgstr "Lịch thời tiết" + +#: ../plugins/copy-tool/org-gnome-copy-tool.eplug.xml.h:1 +msgid "" +"A test plugin which demonstrates a popup menu plugin which lets you copy " +"things to the clipboard." +msgstr "" +"Một trình cầm phít thử ra mà biểu diễn một trình cầm phít trình đơn bật lên " +"cho phép bạn sao chép điều vào bảng tạm." + +#: ../plugins/copy-tool/org-gnome-copy-tool.eplug.xml.h:2 +msgid "Copy _Email Address" +msgstr "Chép địa chỉ th_ư" + +#: ../plugins/copy-tool/org-gnome-copy-tool.eplug.xml.h:3 +msgid "Copy tool" +msgstr "Công cụ chép" + +#: ../plugins/default-mailer/apps-evolution-mail-prompts-checkdefault.schemas.in.in.h:1 +msgid "Check whether Evolution is the default mailer" +msgstr "Kiểm tra nếu Evolution là trình thư mặc định." + +#: ../plugins/default-mailer/apps-evolution-mail-prompts-checkdefault.schemas.in.in.h:2 +msgid "" +"Every time Evolution starts, check whether or not it is the default mailer." +msgstr "Mỗi lần khởi chạy Evolution, kiểm tra nếu nó là trình thư mặc định." + +#: ../plugins/default-mailer/org-gnome-default-mailer.eplug.xml.h:1 +msgid "Checks whether Evolution is the default mail client on startup." +msgstr "Kiểm tra nếu Evolution là trình thư mặc định, khi khởi chạy." + +#: ../plugins/default-mailer/org-gnome-default-mailer.eplug.xml.h:2 +msgid "Default Mail Client " +msgstr "Trình khách thư mặc định" + +#: ../plugins/default-mailer/org-gnome-default-mailer.error.xml.h:1 +msgid "Do you want to make Evolution your default e-mail client?" +msgstr "" +"Bạn có muốn đặt Evolution là trình khách thư điện tử mặc định của bạn không?" + +#: ../plugins/default-source/default-source.c:108 +msgid "Mark as default folder" +msgstr "Đánh dấu là thư mục mặc định" + +#: ../plugins/exchange-operations/e-foreign-folder-dialog.glade.h:1 +#: ../shell/e-foreign-folder-dialog.glade.h:1 +msgid "Open Other User's Folder" +msgstr "Mở thư mục của người dùng khác" + +#: ../plugins/exchange-operations/e-foreign-folder-dialog.glade.h:2 +#: ../shell/e-foreign-folder-dialog.glade.h:2 +msgid "_Account:" +msgstr "Tài _khoản:" + +#: ../plugins/exchange-operations/e-foreign-folder-dialog.glade.h:3 +#: ../shell/e-foreign-folder-dialog.glade.h:3 +msgid "_Folder Name:" +msgstr "T_ên thư mục:" + +#: ../plugins/exchange-operations/e-foreign-folder-dialog.glade.h:4 +#: ../src/gnome-schedule.glade.h:72 +msgid "_User:" +msgstr "_Người dùng:" + +#: ../camel/camel-exchange-provider.c:94 +msgid "Secure Password" +msgstr "Mật khẩu bảo mật" + +#: ../camel/camel-exchange-provider.c:97 +msgid "" +"This option will connect to the Exchange server using secure password (NTLM) " +"authentication." +msgstr "" +"Tùy chọn này sẽ kết nối đến máy phục vụ Exchange dùng cách xác thực mặt khẩu " +"bảo mật (NTLM)." + +#: ../plugins/exchange-operations/exchange-account-setup.c:74 +#: ../camel/camel-exchange-provider.c:105 +msgid "Plaintext Password" +msgstr "Mật khẩu chữ thô" + +#: ../plugins/exchange-operations/exchange-account-setup.c:76 +#: ../camel/camel-exchange-provider.c:107 +msgid "" +"This option will connect to the Exchange server using standard plaintext " +"password authentication." +msgstr "" +"Tùy chọn này sẽ kết nối tới máy phục vụ Exchange dùng cách xác thực mật khẩu " +"chữ thô (không mã hóa)." + +#: ../plugins/exchange-operations/exchange-account-setup.c:255 +#: ../plugins/exchange-operations/exchange-account-setup.c:257 +msgid "Out Of Office" +msgstr "Ở ngoại văn phòng" + +#: ../plugins/exchange-operations/exchange-account-setup.c:262 +#: ../plugins/exchange-operations/exchange-account-setup.c:264 +msgid "" +"The message specified below will be automatically sent to \n" +"each person who sends mail to you while you are out of the office." +msgstr "" +"Thông điệp dưới đây sẽ được gởi tự động\n" +"tới mỗi người gởi thư cho bạn khi bạn ở ngoài văn phòng." + +#: ../plugins/exchange-operations/exchange-account-setup.c:281 +msgid "I am out of the office" +msgstr "Tôi hiện thời ở ngoài văn phòng" + +#: ../plugins/exchange-operations/exchange-account-setup.c:280 +msgid "I am in the office" +msgstr "Tôi đang ở trong văn phòng" + +#: ../plugins/exchange-operations/exchange-account-setup.c:327 +msgid "Change the password for Exchange account" +msgstr "Thay đổi mật khẩu cho tài khoản Exchange" + +#: users/interface.c:478 ../capplets/about-me/gnome-about-me.glade.h:20 +msgid "Change Password" +msgstr "Đổi mật khẩu" + +#: ../plugins/exchange-operations/exchange-account-setup.c:333 +msgid "Manage the delegate settings for Exchange account" +msgstr "Quản lý thiết lập ủy nhiệm cho tài khoản Exchange" + +#: ../plugins/exchange-operations/exchange-account-setup.c:335 +msgid "Delegation Assitant" +msgstr "Trợ tá ủy nhiệm" + +#. Miscelleneous settings +#: ../plugins/exchange-operations/exchange-account-setup.c:347 +#: ../plugins/exchange-operations/exchange-account-setup.c:345 +msgid "Miscelleneous" +msgstr "Linh tinh" + +#: ../plugins/exchange-operations/exchange-account-setup.c:355 +msgid "View the size of all Exchange folders" +msgstr "Xem kích thước của mọi thư mục Exchange" + +#: ../plugins/exchange-operations/exchange-account-setup.c:359 +#: ../plugins/exchange-operations/exchange-account-setup.c:357 +msgid "Folders Size" +msgstr "Cỡ thư mục" + +#: ../plugins/exchange-operations/exchange-account-setup.c:364 +msgid "Exchange Settings" +msgstr "Thiết lập Exchange" + +#: ../plugins/exchange-operations/exchange-account-setup.c:607 +msgid "_OWA Url:" +msgstr "Địa chỉ Mạng _OWA:" + +#: ../plugins/exchange-operations/exchange-account-setup.c:632 +msgid "A_uthenticate" +msgstr "_Xác thực" + +#: ../libgnetwork/gnetwork-tcp-connection.c:1402 +msgid "Authentication Type" +msgstr "Kiểu xác thực" + +#: ../plugins/exchange-operations/exchange-account-setup.c:838 +msgid "Ch_eck for Supported Types" +msgstr "_Kiểm tra có kiểu được hỗ trợ " + +#: ../plugins/exchange-operations/exchange-contacts.c:162 +msgid "" +"Evolution is in offline mode. You cannot create or modify folders now.\n" +"Please switch to online mode for such operations." +msgstr "" +"Trình Evolution hiện thời trong chế độ ngoại tuyện. Như thế thì bạn chưa có " +"thể tạo hay sửa đổi thư mục.\n" +"Hãy chuyển đổi sang chế độ trực tuyến cho thao tác như vậy." + +#: ../storage/exchange-change-password.c:114 +msgid "" +"The current password does not match the existing password for your account. " +"Please enter the correct password" +msgstr "" +"Mật khẩu hiện thời không trùng với mật khẩu đã có của tài khoản. Hãy gõ mật " +"khẩu chính xác" + +#: ../storage/exchange-change-password.c:121 +msgid "The two passwords do not match. Please re-enter the passwords." +msgstr "Hai mật khẩu không trùng nhau. Hãy gõ lại mật khẩu." + +#: ../storage/exchange-change-password.glade.h:3 ../ui/user_info.glade.h:18 +msgid "Confirm Password:" +msgstr "Xác nhận mật khẩu:" + +#: ../storage/exchange-change-password.glade.h:4 +msgid "Current Password:" +msgstr "Mật khẩu hiện thời:" + +#: ../ui/user_info.glade.h:43 +msgid "New Password:" +msgstr "Mật khẩu mới:" + +#: ../storage/exchange-change-password.glade.h:6 +msgid "Your current password has expired. Please change your password now." +msgstr "" +"Mật khẩu hiện thời của bạn đã hết hạn. Hãy thay đổi mật khẩu bạn ngay bây " +"giờ." + +#: src/mine/cmine.cc:310 ../src/preferences.c:147 ../app/core/core-enums.c:390 +#: ../objects/custom/custom.c:117 libexif/canon/mnote-canon-entry.c:302 +#: libexif/pentax/mnote-pentax-entry.c:74 +msgid "Custom" +msgstr "Tự chọn" + +#: ../storage/exchange-delegates.glade.h:4 +msgid "Delegate Permissions" +msgstr "Quyền hạn ủy nhiệm" + +#: ../storage/exchange-permissions-dialog.c:179 +#, c-format +msgid "Permissions for %s" +msgstr "Quyền hạn cho %s" + +#: ../plugins/exchange-operations/exchange-delegates.c:421 +#: ../storage/exchange-delegates.c:419 +msgid "Delegate To" +msgstr "Ủy nhiệm cho" + +#: ../storage/exchange-delegates.c:563 +#, c-format +msgid "Remove the delegate %s?" +msgstr "Gỡ bỏ người được ủy nhiệm %s không?" + +#: ../plugins/exchange-operations/exchange-delegates.c:679 +msgid "Could not access Active Directory" +msgstr "Không thể truy cập thư mục hoạt động." + +#: ../plugins/exchange-operations/exchange-delegates.c:694 +msgid "Could not find self in Active Directory" +msgstr "Không tìm thấy chính nó trong thư mục hoạt động." + +#: ../plugins/exchange-operations/exchange-delegates.c:707 +#, c-format +msgid "Could not find delegate %s in Active Directory" +msgstr "Không tìm thấy người được ủy nhiệm « %s » trong thư mục hoạt động." + +#: ../storage/exchange-delegates.c:720 +#, c-format +msgid "Could not remove delegate %s" +msgstr "Không thể gỡ bỏ người được ủy nhiệm %s" + +#: ../plugins/exchange-operations/exchange-delegates.c:779 +#: ../storage/exchange-delegates.c:780 +msgid "Could not update list of delegates." +msgstr "Không thể cập nhật danh sách các người được ủy nhiệm." + +#: ../storage/exchange-delegates.c:798 +#, c-format +msgid "Could not add delegate %s" +msgstr "Không thể thêm người được ủy nhiệm %s" + +#: ../plugins/exchange-operations/exchange-delegates.c:965 +#: ../storage/exchange-delegates.c:967 +msgid "Error reading delegates list." +msgstr "Gặp lỗi khi đọc danh sách các người được ủy nhiệm." + +#: ../plugins/exchange-operations/exchange-delegates.glade.h:1 +#: ../storage/exchange-delegates.glade.h:1 +msgid "Author (read, create)" +msgstr "Tác giả (đọc, tạo)" + +#. Translators: This is used for permissions for for the folder Calendar. +#: ../plugins/exchange-operations/exchange-delegates.glade.h:3 +#: ../src/zenity.glade.h:7 +msgid "C_alendar:" +msgstr "_Lịch:" + +#. Translators: This is used for permissions for for the folder Contacts. +#: ../storage/exchange-delegates.glade.h:3 +msgid "Co_ntacts:" +msgstr "_Liên lạc:" + +#: ../storage/exchange-delegates.glade.h:5 +msgid "Delegates" +msgstr "Người được ủy nhiệm" + +#: ../storage/exchange-delegates.glade.h:6 +msgid "Editor (read, create, edit)" +msgstr "Người sửa (đọc, tạo, sửa đổi)" + +#: ../storage/exchange-delegates.glade.h:8 +msgid "Permissions for" +msgstr "Quyền hạn cho" + +#: ../storage/exchange-delegates.glade.h:9 +msgid "Reviewer (read-only)" +msgstr "Người xem lại (chỉ đọc)" + +#: ../storage/exchange-delegates.glade.h:10 +msgid "" +"These users will be able to send mail on your behalf\n" +"and access your folders with the permissions you give them." +msgstr "" +"Những người dùng này sẽ có thể gởi thư điện tử\n" +"thay mặt cho bạn, cũng có thể truy cập các thư mục bạn,\n" +"dùng quyền hạn mà bạn đã cho họ." + +#: ../storage/exchange-delegates.glade.h:12 +msgid "_Delegate can see private items" +msgstr "Người ủ_y nhiêm có thể thấy những mục tư nhân" + +#: ../storage/exchange-delegates.glade.h:15 +msgid "_Tasks:" +msgstr "_Tác vụ" + +#: ../plugins/exchange-operations/exchange-folder-permission.c:58 +#: ../plugins/exchange-operations/org-gnome-folder-permissions.xml.h:2 +msgid "Permissions..." +msgstr "Quyền hạn..." + +#: ../plugins/exchange-operations/exchange-folder-size-display.c:136 +#: ../storage/exchange-folder-size.c:322 +msgid "Folder Name" +msgstr "Tên thư mục" + +#: ../plugins/exchange-operations/exchange-folder-size-display.c:140 +#: ../storage/exchange-folder-size.c:326 +msgid "Folder Size" +msgstr "Cỡ thư mục" + +#: ../plugins/exchange-operations/exchange-folder-subscription.c:181 +#: ../plugins/exchange-operations/org-gnome-folder-subscription.xml.h:1 +msgid "Subscribe to Other User's Folder" +msgstr "Đăng ký với thư mục của người dùng khác" + +#: ../plugins/exchange-operations/exchange-folder-tree.glade.h:1 +#: ../storage/exchange-folder-tree.glade.h:1 +msgid "Exchange Folder Tree" +msgstr "Cây thư mục Exchange" + +#: ../plugins/exchange-operations/exchange-folder.c:110 +msgid "Unsubscribe Folder..." +msgstr "Bỏ đăng ký thư mục..." + +#: ../shell/e-folder-misc-dialogs.c:766 +#, c-format +msgid "Really unsubscribe from folder \"%s\"?" +msgstr "Thật sự bỏ đăng ký trên thư mục « %s » không?" + +#: ../shell/e-folder-misc-dialogs.c:780 +#, c-format +msgid "Unsubscribe from \"%s\"" +msgstr "Bỏ đăng ký trên thư mục « %s »" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.c:301 +#: ../storage/exchange-permissions-dialog.c:295 +msgid "(Permission denied.)" +msgstr "(Không đủ quyền truy cập.)" + +#: ../storage/exchange-permissions-dialog.c:402 +msgid "Add User:" +msgstr "Thêm người dùng:" + +#: ../storage/exchange-permissions-dialog.c:402 ../src/gtkfunc.c:101 +msgid "Add User" +msgstr "Thêm người dùng" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:2 +#: ../storage/exchange-permissions-dialog.glade.h:2 +msgid "Permissions" +msgstr "Quyền hạn" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:3 +#: ../storage/exchange-permissions-dialog.glade.h:3 +msgid "Cannot Delete" +msgstr "Không thể xoá bỏ" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:4 +#: ../storage/exchange-permissions-dialog.glade.h:4 +msgid "Cannot Edit" +msgstr "Không thể sửa" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:5 +#: ../storage/exchange-permissions-dialog.glade.h:5 +msgid "Create items" +msgstr "Tạo mục" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:6 +#: ../storage/exchange-permissions-dialog.glade.h:6 +msgid "Create subfolders" +msgstr "Tạo thư mục con" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:7 +#: ../storage/exchange-permissions-dialog.glade.h:7 +msgid "Delete Any Items" +msgstr "Xoá bỏ mọi mục" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:8 +#: ../storage/exchange-permissions-dialog.glade.h:8 +msgid "Delete Own Items" +msgstr "Xoá bỏ mục mình" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:9 +#: ../storage/exchange-permissions-dialog.glade.h:9 +msgid "Edit Any Items" +msgstr "Sửa mọi mục" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:10 +#: ../storage/exchange-permissions-dialog.glade.h:10 +msgid "Edit Own Items" +msgstr "Sửa mục mình" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:11 +#: ../storage/exchange-permissions-dialog.glade.h:11 +msgid "Folder contact" +msgstr "Liên lạc thư mục" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:12 +#: ../storage/exchange-permissions-dialog.glade.h:12 +msgid "Folder owner" +msgstr "Người sở hữu thư mục" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:13 +msgid "Folder visible" +msgstr "Hiển thị thư mục" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:14 +#: ../storage/exchange-permissions-dialog.glade.h:14 +msgid "Read items" +msgstr "Mục đã đọc" + +#: ../plugins/exchange-operations/exchange-permissions-dialog.glade.h:15 +#: ../storage/exchange-permissions-dialog.glade.h:15 +msgid "Role: " +msgstr "Vai trò : " + +#: ../plugins/exchange-operations/exchange-user-dialog.c:144 +#: ../servers/exchange/lib/e2k-user-dialog.c:144 ../lib/e2k-user-dialog.c:144 +msgid "Select User" +msgstr "Chọn người dùng" + +#: ../lib/e2k-user-dialog.c:182 +msgid "Addressbook..." +msgstr "Sổ địa chỉ..." + +#: ../plugins/exchange-operations/org-gnome-exchange-ab-subscription.xml.h:1 +msgid "Subscribe to Other User's Contacts" +msgstr "Đăng ký với các liên lạc của người dùng khác" + +#: ../plugins/exchange-operations/org-gnome-exchange-cal-subscription.xml.h:1 +msgid "Subscribe to Other User's Calendar" +msgstr "Đăng ký với lịch của người dùng khác" + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:1 +msgid "Cannot change password due to configuration problems." +msgstr "Không thể thay đổi mật khẩu vì vấn đề cấu hình." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:2 +msgid "Cannot display folders." +msgstr "Không thể hiển thị thư mục." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:3 +msgid "" +"Changes to Exchange account configuration will take place after you quit and " +"restart Evolution." +msgstr "" +"Các thay đổi trong cấu hình tài khoản Evolution sẽ có tác động sau khi bạn " +"thoát rồi khởi chạy lại trình Evolution." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:4 +msgid "Could not authenticate to server." +msgstr "Không thể xác thực tới máy phục vụ." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:5 +msgid "Could not change password." +msgstr "Không thể thay đổi mật khẩu." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:6 +msgid "" +"Could not configure Exchange account because \n" +"an unknown error occurred. Check the URL, \n" +"username, and password, and try again." +msgstr "" +"Không thể cấu hình tài khoản Exchange\n" +"vì gặp một lỗi lạ. Bạn hãy kiểm tra đã gõ đúng\n" +"địa chỉ Mạng, tên người dùng và mật khẩu\n" +"rồi thử lại." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:9 +msgid "Could not connect to Exchange server." +msgstr "Không thể kết nối đến máy phục vụ Exchange." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:10 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:9 +msgid "Could not connect to server {0}." +msgstr "Không thể kết nối đến máy phục vụ {0}." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:11 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:10 +msgid "Could not determine folder permissions for delegates." +msgstr "Không thể xác định quyền truy cập thư mục cho người được ủy nhiệm." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:12 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:11 +msgid "Could not find Exchange Web Storage System." +msgstr "Không tìm thấy Hệ thống Cất giữ Mạng Exchange." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:13 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:12 +msgid "Could not locate server {0}." +msgstr "Không thể định vị máy phục vụ {0}." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:14 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:13 +msgid "Could not make {0} a delegate" +msgstr "Không thể ủy nhiệm cho « {0} »." + +#: ../storage/exchange-permissions-dialog.c:222 +msgid "Could not read folder permissions" +msgstr "Không thể đọc quyền truy cập thư mục." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:16 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:15 +msgid "Could not read folder permissions." +msgstr "Không thể đọc quyền truy cập thư mục." + +#: ../storage/exchange-oof.c:424 +msgid "Could not read out-of-office state" +msgstr "Không thể đọc trạng thái ngoài-văn-phòng." + +#: ../storage/exchange-permissions-dialog.c:264 +msgid "Could not update folder permissions." +msgstr "Không thể cập nhật quyền truy cập thư mục." + +#: ../storage/exchange-oof.c:199 ../storage/exchange-oof.c:406 +msgid "Could not update out-of-office state" +msgstr "Không thể cập nhật tính trạng ngoài-văn-phòng." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:20 +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:19 +msgid "Exchange Account is offline." +msgstr "Tài khoản Exchange ngoại tuyến." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:21 +msgid "" +"Exchange Connector requires access to certain\n" +"functionality on the Exchange Server that appears\n" +"to be disabled or blocked. (This is usually \n" +"unintentional.) Your Exchange Administrator will \n" +"need to enable this functionality in order for \n" +"you to be able to use Ximian Connector.\n" +"\n" +"For information to provide to your Exchange \n" +"administrator, please follow the link below:\n" +"\n" +"{0}\n" +" " +msgstr "" +"Trình Exchange Connector cần thiết truy cập\n" +"chức năng nào đó trên máy phục vụ Exchange\n" +"dương như bị tắt hay bị trở ngại.\n" +"(Thường không phải do chủ tâm.)\n" +"Quản trị Exchange của bạn sẽ cần phải hiệu lực\n" +"chức năng này để cho phép bạn sử dụng trình\n" +"Ximian Connector.\n" +"\n" +"Để xem thông tin cần cung cấp cho quản trị Exchange,\n" +"hãy theo liên kết bên dưới đây:\n" +"\n" +"<{0}>\n" +" " + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:33 +msgid "Failed to update delegates:" +msgstr "Không thể cập nhật người được ủy nhiệm:" + +#: ../mail/mail-stub-exchange.c:2356 ../storage/xc-commands.c:313 +msgid "Folder already exists" +msgstr "Thư mục đã có" + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:35 +#: ../storage/xc-commands.c:316 +msgid "Folder does not exist" +msgstr "Không có thư mục này." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:36 +msgid "Folder offline" +msgstr "Thư mục này ngoại tuyến." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:37 +#: ../shell/e-shell.c:1263 +msgid "Generic error" +msgstr "Lỗi chung chung" + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:38 +msgid "" +"If OWA is running on a different path, you must specify that in the account " +"configuration dialog." +msgstr "" +"Nếu OWA đang chạy trên đường dẫn khác thì bạn cần phải ghi rõ nó trong hộp " +"thoại cấu hình tài khoản." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:39 +msgid "Mailbox for {0} is not on this server." +msgstr "Không có hộp thư cho « {0} » trên máy phục vụ này." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:40 +msgid "Make sure the URL is correct and try again." +msgstr "Hãy kiểm tra xem gõ địa chỉ Mạng đúng, rồi thử lại." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:41 +msgid "Make sure the server name is spelled correctly and try again." +msgstr "Hãy kiểm tra xem gõ tên phục vụ đúng, rồi thử lại." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:42 +msgid "Make sure the username and password are correct and try again." +msgstr "Hãy kiểm tra xem gõ tên người dùng và mật khẩu đúng, rồi thử lại." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:43 +msgid "No Global Catalog server configured for this account." +msgstr "" +"Không có trình phục vụ Phân loại Toàn cục được cấu hình cho tài khoản này." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:44 +msgid "No mailbox for user {0} on {1}." +msgstr "Không có hộp thư cho người dùng « {0} » trên « {1} »." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:45 +msgid "No such user {0}" +msgstr "Không có người dùng như vậy « {0} »." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:46 +msgid "Password successfully changed." +msgstr "Mật khẩu đã được thay đổi." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:48 +msgid "Please restart Evolution" +msgstr "Hãy khởi chạy lại Evolution" + +#: ../shell/e-folder-misc-dialogs.c:451 +msgid "Please select a user." +msgstr "Hãy chọn một người dùng." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:50 +msgid "Server rejected password because it is too weak." +msgstr "Máy phục vụ đã từ chối mật khẩu vì quá yếu." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:51 +#: ../storage/exchange-config-listener.c:593 +msgid "The Exchange account will be disabled when you quit Evolution" +msgstr "Tài khoản Exchange sẽ bị tắt khi bạn thoát khỏi trình Evolution." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:52 +#: ../storage/exchange-config-listener.c:588 +msgid "The Exchange account will be removed when you quit Evolution" +msgstr "Tài khoản Exchange sẽ bị gỡ bỏ khi bạn thoát khỏi trình Evolution" + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:53 +msgid "The Exchange server is not compatible with Exchange Connector." +msgstr "Tài khoản Exchange không tương thích với Exchange Connector." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:54 +msgid "" +"The server is runinng Exchange 5.5. Exchange Connector \n" +"supports Microsoft Exchange 2000 and 2003 only." +msgstr "" +"Máy phục vụ có chạy phần mềm Exchange phiên bản 5.5.\n" +"Exchange Connector hỗ trợ chỉ Exchange phiên bản 2000 và 2003." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:56 +msgid "" +"This probably means that your server requires \n" +"you to specify the Windows domain name \n" +"as part of your username (eg, "DOMAIN\\user").\n" +"\n" +"Or you might have just typed your password wrong." +msgstr "" +"Rất có thể có nghĩa là máy phục vụ cần thiết\n" +"bạn ghi rõi tên miền Windows là phần của tên người dùng\n" +"(v.d. «MIỀN\\người_dùng»).\n" +"\n" +"Hoạc có lẽ bạn đã gõ sai mật khẩu." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:61 +msgid "Try again with a different password." +msgstr "Hãy thử lại với một mật khẩu khác." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:62 +msgid "Unable to add user to access control list:" +msgstr "Không thể thêm người dùng vào danh sách điều khiển truy cập." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:63 +msgid "Unable to edit delegates." +msgstr "Không thể hiệu chỉnh người được ủy nhiệm." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:64 +msgid "Unknown error looking up {0}" +msgstr "Gặp lỗi lạ khi tra cứu « {0} »." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:65 +msgid "Unknown error." +msgstr "Không biết lỗi đó." + +#: ../extensions/page-info/page-info-dialog.c:776 +msgid "Unknown type" +msgstr "Kiểu lạ" + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:67 +#: src/err-codes.h:152 +msgid "Unsupported operation" +msgstr "Thao tác không được hỗ trợ" + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:68 +msgid "You are nearing your quota available for storing mails on this server." +msgstr "Bạn gần vượt quá giới hạn lưu thư trên máy phục vụ này." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:69 +#: ../storage/exchange-delegates.c:445 +msgid "You cannot make yourself your own delegate" +msgstr "Bạn không thể ủy nhiệm cho mình." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:70 +msgid "You have exceeded your quota for storing mails on this server." +msgstr "Bạn đã vượt quá giới hạn lưu thư trên máy phục vụ này." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:71 +msgid "You may only configure a single Exchange account." +msgstr "Bạn có thể cấu hình chỉ một tài khoản Exchange riêng lẻ." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:72 +msgid "" +"Your current usage is : {0}KB. Try to clear up some space by deleting some " +"mails." +msgstr "" +"Hiện thời bạn đang sử dụng chỗ : {0}KB. Hãy cố giải phóng thêm chỗ trống " +"bằng cách xoá bỏ một số thư." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:73 +msgid "" +"Your current usage is : {0}KB. You will not be able to either send or " +"recieve mails now." +msgstr "" +"Hiện thời bạn đang sử dụng chỗ : {0}KB. Vậy bạn sẽ không thể gởi hay nhận " +"thư." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:74 +msgid "" +"Your current usage is : {0}KB. You will not be able to send mails till you " +"clear up some space by deleting some mails." +msgstr "" +"Hiện thời bạn đang sử dụng chỗ : {0}KB. Bạn sẽ không thể gởi thư đến khi bạn " +"giải phóng thêm chỗ trống bằng cách xoá bỏ một số thư." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:75 +msgid "Your password has expired." +msgstr "Mật khẩu bạn đã hết hạn." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:77 +msgid "{0} cannot be added to an access control list" +msgstr "Không thể thêm « {0} » vào danh sách điều khiển truy cập." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:78 +msgid "{0} is already a delegate" +msgstr "« {0} » đã một người được ủy nhiệm." + +#: ../plugins/exchange-operations/org-gnome-exchange-operations.error.xml.h:79 +msgid "{0} is already in the list" +msgstr "« {0} » đã có trong danh sách." + +#: ../plugins/exchange-operations/org-gnome-exchange-tasks-subscription.xml.h:1 +msgid "Subscribe to Other User's Tasks" +msgstr "Đăng ký với các tác vụ của người dùng khác" + +#: ../plugins/exchange-operations/org-gnome-folder-permissions.xml.h:1 +msgid "Check folder permissions" +msgstr "Hãy kiểm tra quyền truy cập thư mục là đúng." + +#: ../plugins/folder-unsubscribe/folder-unsubscribe.c:57 +#, c-format +msgid "Unsubscribing from folder \"%s\"" +msgstr "Đang bỏ đăng ký trên thư mục « %s »" + +#: ../plugins/folder-unsubscribe/org-gnome-mail-folder-unsubscribe.eplug.xml.h:1 +msgid "Allows unsubscribing of mail folders in the folder tree context menu." +msgstr "Cho phép bỏ đăng ký thư mục thư trong trình đơn ngữ cảnh cây thư mục." + +#: ../plugins/folder-unsubscribe/org-gnome-mail-folder-unsubscribe.eplug.xml.h:2 +msgid "Unsubscribe Folders" +msgstr "Bỏ đăng ký thư mục" + +#: ../plugins/groupwise-account-setup/camel-gw-listener.c:414 +msgid "Checklist" +msgstr "Danh sách kiểm" + +#: ../plugins/groupwise-account-setup/org-gnome-gw-account-setup.eplug.xml.h:1 +msgid "Groupwise Account Setup" +msgstr "Thiết lập tài khoản Groupwise" + +#: ../plugins/groupwise-features/junk-mail-settings.c:77 +msgid "Junk Settings" +msgstr "Thiết lập Rác" + +#: ../plugins/groupwise-features/junk-mail-settings.c:90 +msgid "Junk Mail Settings" +msgstr "Thiết lập Thư Rác" + +#: ../plugins/groupwise-features/junk-mail-settings.c:112 +msgid "Junk Mail Settings..." +msgstr "Thiết lập Thư Rác.." + +#: ../plugins/groupwise-features/junk-settings.glade.h:1 +msgid "Junk List :" +msgstr "Danh sách Rác" + +#: ../plugins/groupwise-features/junk-settings.glade.h:2 +#: ../src/red_activation.py:57 +msgid "Email:" +msgstr "Địa chỉ thư :" + +#: ../plugins/groupwise-features/junk-settings.glade.h:3 +msgid "Junk Mail Settings" +msgstr "Thiết lập Thư Rác" + +#: ../plugins/groupwise-features/junk-settings.glade.h:5 +#: ../plugins/mail-account-disable/mail-account-disable.c:46 +msgid "_Disable" +msgstr "_Tắt" + +#: ../plugins/groupwise-features/junk-settings.glade.h:6 +msgid "_Enable" +msgstr "_Bật" + +#: ../plugins/groupwise-features/junk-settings.glade.h:7 +msgid "_Junk List" +msgstr "Danh sách _Rác" + +#: ../plugins/groupwise-features/org-gnome-compose-send-options.xml.h:1 +msgid "Add Send Options to groupwise messages" +msgstr "Thêm Tùy chọn Gởi vào mọi thư Groupwise" + +#: ../widgets/misc/e-send-options.glade.h:17 +msgid "Send Options" +msgstr "Tùy chọn gởi" + +#: ../plugins/groupwise-features/org-gnome-groupwise-features.eplug.xml.h:1 +msgid "A plugin for the features in Groupwise accounts." +msgstr "Một trình cầm phít cho những tính năng trong tài khoản Groupwise." + +#: ../plugins/groupwise-features/org-gnome-groupwise-features.eplug.xml.h:2 +msgid "Groupwise Features" +msgstr "Tính năng Groupwise" + +#: ../plugins/groupwise-features/process-meeting.c:49 +msgid "Accept Tentatively" +msgstr "Chấp nhận tạm" + +#: ../plugins/groupwise-features/properties.glade.h:2 +msgid "Users :" +msgstr "Người dùng: " + +#: ../plugins/groupwise-features/properties.glade.h:6 +msgid "Shared Folder Notification" +msgstr "Thông báo thư mục chung" + +#: ../plugins/groupwise-features/properties.glade.h:8 +msgid "The participants will receive the following notification.\n" +msgstr "Mọi người dự sẽ nhận thông báo theo đây.\n" + +#: ../plugins/groupwise-features/properties.glade.h:11 +msgid "_Contacts..." +msgstr "_Liên lạc..." + +#: ../plugins/groupwise-features/properties.glade.h:12 +msgid "_Cutomize notification message" +msgstr "Tự _chọn thông điệp thông báo" + +#: ../plugins/groupwise-features/properties.glade.h:13 +msgid "_Not Shared" +msgstr "_Không chung" + +#: ../plugins/groupwise-features/properties.glade.h:15 +msgid "_Shared With ..." +msgstr "_Chung với ..." + +#: ../plugins/groupwise-features/properties.glade.h:16 +msgid "_Sharing" +msgstr "Dùng _chung" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:1 +#: ../data/gnome-keyring-manager.glade.h:2 ../gnomecard/card-editor.glade.h:4 +msgid "Name" +msgstr "Tên" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:2 +msgid "Access Rights" +msgstr "Quyền truy cập" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:3 +msgid "Add/Edit" +msgstr "Thêm/Hiệu chỉnh" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:5 +msgid "Con_tacts" +msgstr "_Liên lạc" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:7 +msgid "Modify _folders/options/rules/" +msgstr "Sửa đổi _thư mục/tùy chọn/quy tắc/" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:8 +msgid "Read items marked _private" +msgstr "Đọc mục có dấu _Riêng" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:9 +msgid "Reminder Notes" +msgstr "Chú thích nhắc nhở" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:10 +msgid "Subscribe to my _alarms" +msgstr "Đăng ký với _báo động tôi" + +#: ../plugins/groupwise-features/proxy-add-dialog.glade.h:11 +msgid "Subscribe to my _notifications" +msgstr "Đăng ký với _thông báo tôi" + +#: ../src/dialogs.c:891 ../src/dialogs.c:1111 ../gmedia_slice/interface.c:433 +msgid "_Write" +msgstr "_Ghi" + +#: ../plugins/groupwise-features/proxy-listing.glade.h:1 network.c:1364 +#: ../src/red_prefs.py:210 +msgid "Proxy" +msgstr "Ủy nhiệm" + +#: ../src/f-spot.glade.h:183 ../gnomecard/gnomecard.glade.h:5 +msgid "dialog1" +msgstr "thoại1" + +#: ../plugins/groupwise-features/proxy-login-dialog.glade.h:1 +msgid "Account Name" +msgstr "Tên tài khoản" + +#: ../plugins/groupwise-features/proxy-login-dialog.glade.h:2 +msgid "Proxy Login" +msgstr "Đăng nhập ủy nhiệm" + +#: ../plugins/groupwise-features/proxy.c:490 +#, c-format +msgid "%sEnter password for %s (user %s)" +msgstr "%sNhập mật khẩu cho « %s » (người dùng « %s »)" + +#: ../plugins/groupwise-features/proxy-login.c:505 +#: ../plugins/groupwise-features/proxy-login.c:485 +msgid "_Proxy Login..." +msgstr "Đăng nhập ủ_y nhiệm.." + +#: ../plugins/groupwise-features/proxy.c:698 +msgid "The Proxy tab will be available only when the account is online." +msgstr "Thanh Ủy nhiệm sẽ sẵn sàng chỉ khi tài khoản trực tuyến." + +#: ../plugins/groupwise-features/proxy.c:703 +#: ../plugins/groupwise-features/proxy.c:672 +msgid "The Proxy tab will be available only when the account is enabled." +msgstr "Thanh Ủy nhiệm sẽ sẵn sàng chỉ khi tài khoản đ" + +#: src/fe-gtk/chanlist.c:600 ../glom/glom.glade.h:162 +#: ../providers/ibmdb2/gda-ibmdb2-provider.c:976 +#: ../providers/oracle/gda-oracle-provider.c:1848 po/silky-channel.glade.h:26 +msgid "Users" +msgstr "Người dùng" + +#: ../plugins/groupwise-features/share-folder-common.c:319 +#: ../plugins/groupwise-features/share-folder-common.c:318 +msgid "Enter the users and set permissions" +msgstr "Nhập những người dùng và lập quyền hạn" + +#: ../plugins/groupwise-features/share-folder-common.c:338 +#: ../plugins/groupwise-features/share-folder-common.c:337 +msgid "New _Shared Folder..." +msgstr "Thư mục _chung mới..." + +#: ../plugins/groupwise-features/share-folder-common.c:446 +#: ../files/Nautilus_View_sharing_properties.server.in.in.h:2 +msgid "Sharing" +msgstr "Chia sẻ" + +#: ../plugins/groupwise-features/status-track.c:235 +msgid "Track Message Status..." +msgstr "Theo dõi trạng thái thư..." + +#: ../plugins/hula-account-setup/org-gnome-evolution-hula-account-setup.eplug.xml.h:1 +msgid "A plugin to setup hula calendar sources." +msgstr "Bộ cầm phít có thể thiết lập nguồn lịch « hula »." + +#: ../plugins/hula-account-setup/org-gnome-evolution-hula-account-setup.eplug.xml.h:2 +msgid "Hula Account Setup" +msgstr "Thiết lập tài khoản Hula" + +#: ../plugins/save-calendar/ical-format.c:136 +msgid "iCalendar format (.ics)" +msgstr "Tập tin iCalendar (.ics)" + +#: ../plugins/ipod-sync/org-gnome-ipod-sync-evolution.eplug.xml.h:1 +msgid "Synchronize the selected task/calendar/addressbook with Apple iPod" +msgstr "Đồng bộ hóa sổ địa chỉ/lịch/tác vụ đã chọn với Apple iPod" + +#: ../plugins/ipod-sync/org-gnome-ipod-sync-evolution.eplug.xml.h:2 +msgid "Synchronize to iPod" +msgstr "Đồng bộ tới iPod" + +#: ../plugins/ipod-sync/org-gnome-ipod-sync-evolution.eplug.xml.h:3 +msgid "iPod Synchronization" +msgstr "Đồng bộ iPod" + +#: ../plugins/ipod-sync/sync.c:158 +msgid "No output directory!" +msgstr "• Không có thư mục xuất. •" + +#: ../plugins/ipod-sync/sync.c:159 +msgid "" +"The output directory was not found on iPod! Please ensure that iPod has been " +"correctly set up and try again." +msgstr "" +"Không tìm thấy thư mục xuất trên iPod. Hãy chắc đã thiết lập đúng iPod rồi " +"thử lại." + +#: ../plugins/ipod-sync/sync.c:174 ../plugins/ipod-sync/sync.c:202 +msgid "Could not export data!" +msgstr "• Không thể xuất dữ liệu. •" + +#: ../plugins/ipod-sync/sync.c:203 +msgid "Exporting data failed." +msgstr "Lỗi xuất dữ liệu." + +#: ../plugins/ipod-sync/sync.c:231 +msgid "Could not open addressbook!" +msgstr "• Không thể mở sổ địa chỉ. •" + +#: ../plugins/ipod-sync/sync.c:232 +msgid "Could not open the Evolution addressbook to export data." +msgstr "Không thể mở Sổ địa chỉ Evolution để xuất dữ liệu." + +#: ../plugins/ipod-sync/sync.c:291 +msgid "Could not open calendar/todo!" +msgstr "• Không thể mở lịch/cần làm. •" + +#: ../plugins/ipod-sync/sync.c:292 +msgid "Could not open the Evolution calendar/todo list to export data." +msgstr "Không thể mở lịch/danh sách cần làm để xuất dữ liệu." + +#: ../plugins/itip-formatter/itip-formatter.c:724 +msgid "Unable to parse item" +msgstr "Không thể phân tach mục" + +#: ../plugins/itip-formatter/itip-formatter.c:780 +#, c-format +msgid "Unable to send item to calendar '%s'. %s" +msgstr "Không gởi được mục cho lịch « %s ». %s" + +#: ../plugins/itip-formatter/itip-formatter.c:791 +#, c-format +msgid "Sent to calendar '%s' as accepted" +msgstr "Đã gởi cho lịch « %s »: đã chấp nhận" + +#: ../plugins/itip-formatter/itip-formatter.c:795 +#, c-format +msgid "Sent to calendar '%s' as tentative" +msgstr "Đã gởi cho lịch « %s »: dự định" + +#: ../plugins/itip-formatter/itip-formatter.c:800 +#, c-format +msgid "Sent to calendar '%s' as declined" +msgstr "Đã gởi cho lịch « %s »: bị từ chối" + +#: ../plugins/itip-formatter/itip-formatter.c:805 +#, c-format +msgid "Sent to calendar '%s' as cancelled" +msgstr "Đã gởi cho lịch « %s »: bị hủy bỏ" + +#: ../plugins/itip-formatter/itip-formatter.c:898 +#, c-format +msgid "Organizer has removed the delegate %s " +msgstr "Bộ tổ chức đã gỡ bỏ người được ủy nhiệm %s." + +#: ../plugins/itip-formatter/itip-formatter.c:905 +msgid "Sent a cancellation notice to the delegate" +msgstr "Đã gởi một thông báo hủy bỏ cho người được ủy nhiệm." + +#: ../plugins/itip-formatter/itip-formatter.c:907 +msgid "Could not send the cancellation notice to the delegate" +msgstr "Không gởi được thông báo hủy bỏ cho người được ủy nhiệm.?" + +#: ../plugins/itip-formatter/itip-formatter.c:991 +#: ../plugins/itip-formatter/itip-formatter.c:979 +msgid "Attendee status could not be updated because the status is invalid" +msgstr "Không thể cập nhật trạng thái người dự vì trạng thái không hợp lệ." + +#: ../plugins/itip-formatter/itip-formatter.c:1017 +#: ../plugins/itip-formatter/itip-formatter.c:1005 +#, c-format +msgid "Unable to update attendee. %s" +msgstr "Không thể cập nhật người dự. %s" + +#: ../plugins/itip-formatter/itip-formatter.c:1021 +#: ../plugins/itip-formatter/itip-formatter.c:1009 +msgid "Attendee status updated" +msgstr "Trạng thái người dự đã được cập nhật" + +#: ../plugins/itip-formatter/itip-formatter.c:1148 +#: ../plugins/itip-formatter/itip-formatter.c:1136 +msgid "The calendar attached is not valid" +msgstr "Lịch đã đính kèm không hợp lệ" + +#: ../plugins/itip-formatter/itip-formatter.c:1149 +#: ../plugins/itip-formatter/itip-formatter.c:1137 +msgid "" +"The message claims to contain a calendar, but the calendar is not valid " +"iCalendar." +msgstr "" +"Thư này tuyên bố chứa một lịch, nhưng mà lịch đó không phải là một iCalendar " +"hợp lệ." + +#: ../plugins/itip-formatter/itip-formatter.c:1262 +msgid "The item in the calendar is not valid" +msgstr "Mục đó trong lịch không hợp lệ." + +#: ../plugins/itip-formatter/itip-formatter.c:1263 +msgid "" +"The message does contain a calendar, but the calendar contains no events, " +"tasks or free/busy information" +msgstr "" +"Thư đó có phải chứa một lịch, nhưng mà lịch đó không chứa sự kiện nào, công " +"việc nào hay thông tin rảnh/bận nào." + +#: ../plugins/itip-formatter/itip-formatter.c:1209 +#: ../plugins/itip-formatter/itip-formatter.c:1197 +msgid "The calendar attached contains multiple items" +msgstr "Lịch đã đính kèm chứa nhiều mục" + +#: ../plugins/itip-formatter/itip-formatter.c:1210 +#: ../plugins/itip-formatter/itip-formatter.c:1198 +msgid "" +"To process all of these items, the file should be saved and the calendar " +"imported" +msgstr "Để xử lý mọi mục này thì nên lưu tập tin này và nhập lịch đó." + +#: ../plugins/itip-formatter/itip-formatter.c:1977 +msgid "_Delete message after acting" +msgstr "_Xoá bỏ thư sau hành động" + +#: ../plugins/itip-formatter/itip-formatter.c:2001 +#: ../plugins/itip-formatter/itip-formatter.c:1987 +msgid "Conflict Search" +msgstr "Tìm kiếm xung đột" + +#: ../plugins/itip-formatter/itip-formatter.c:2000 +msgid "Select the calendars to search for meeting conflicts" +msgstr "Chọn những lịch cần tìm kiệm cuộc họp có xung đột với nhau" + +#: ../plugins/itip-formatter/itip-formatter.c:2031 +#: ../plugins/itip-formatter/itip-formatter.c:2017 +msgid "Conflict Search Table" +msgstr "Bảng tìm kiếm xung đột" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a date. +#: ../gtk/gtkfilechooserdefault.c:7037 ../gncal/gnomecal-main-window.c:576 +#: ../storage/sunone-itip-view.c:138 utils.c:1089 +msgid "Today" +msgstr "Hôm nay" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 24-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:187 ../storage/sunone-itip-view.c:143 +#, fuzzy +msgid "Today %H:%M" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Hôm nay %l:%M %p\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"Hôm nay %H:%M" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 24-hour format. +#: ../plugins/itip-formatter/itip-view.c:191 ../storage/sunone-itip-view.c:147 +#, fuzzy +msgid "Today %H:%M:%S" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Hôm nay %l:%M %p\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"Hôm nay %H:%M:%S" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 12-hour format. +#: ../plugins/itip-formatter/itip-view.c:200 ../storage/sunone-itip-view.c:156 +#, fuzzy +msgid "Today %l:%M:%S %p" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Hôm nay %l:%M %p\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"Hôm nay %l:%M:%S %p" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a date. +#: ../plugins/itip-formatter/itip-view.c:210 ../storage/sunone-itip-view.c:166 +msgid "Tomorrow" +msgstr "Ngày mai" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 24-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:215 ../storage/sunone-itip-view.c:171 +msgid "Tomorrow %H:%M" +msgstr "Ngày mai %H:%M" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 24-hour format. +#: ../plugins/itip-formatter/itip-view.c:219 ../storage/sunone-itip-view.c:175 +msgid "Tomorrow %H:%M:%S" +msgstr "Ngày mai %H:%M:%S" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 12-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:224 ../storage/sunone-itip-view.c:180 +msgid "Tomorrow %l:%M %p" +msgstr "Ngày mai %l:%M %p" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a time, +#. in 12-hour format. +#: ../plugins/itip-formatter/itip-view.c:228 ../storage/sunone-itip-view.c:184 +msgid "Tomorrow %l:%M:%S %p" +msgstr "Ngày mai %l:%M:%S %p" + +# Variable: don't translate / Biến: đừng dịch +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday. +#: ../plugins/itip-formatter/itip-view.c:247 ../storage/sunone-itip-view.c:203 +#, c-format +msgid "%A" +msgstr "%A" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 24-hour format, without seconds. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 24-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:252 ../storage/sunone-itip-view.c:208 +msgid "%A %H:%M" +msgstr "%A %H:%M" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 24-hour format. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 24-hour format. +#: ../plugins/itip-formatter/itip-view.c:256 ../storage/sunone-itip-view.c:212 +msgid "%A %H:%M:%S" +msgstr "%A %H:%M:%S" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 12-hour format, without seconds. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 12-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:261 ../storage/sunone-itip-view.c:217 +msgid "%A %l:%M %p" +msgstr "%A %l:%M %p" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 12-hour format. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a +#. time, in 12-hour format. +#: ../plugins/itip-formatter/itip-view.c:266 +#: ../plugins/itip-formatter/itip-view.c:265 ../storage/sunone-itip-view.c:221 +msgid "%A %l:%M:%S %p" +msgstr "%A %l:%M:%S %p" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday and a date +#. without a year. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a date +#. without a year. +#: ../plugins/itip-formatter/itip-view.c:274 ../storage/sunone-itip-view.c:230 +msgid "%A, %B %e" +msgstr "%A, %B %e" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date +#. without a year and a time, +#. in 24-hour format, without seconds. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date +#. without a year and a time, +#. in 24-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:280 ../storage/sunone-itip-view.c:236 +msgid "%A, %B %e %H:%M" +msgstr "%A, %B %e %H:%M" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date without a year +#. and a time, in 24-hour format. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date without a year +#. and a time, in 24-hour format. +#: ../plugins/itip-formatter/itip-view.c:284 ../storage/sunone-itip-view.c:240 +msgid "%A, %B %e %H:%M:%S" +msgstr "%A, %B %e %H:%M:%S" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date without a year +#. and a time, in 12-hour format, without seconds. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date without a year +#. and a time, in 12-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:289 ../storage/sunone-itip-view.c:245 +msgid "%A, %B %e %l:%M %p" +msgstr "%A, %B %e %l:%M %p" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date without a year +#. and a time, in 12-hour format. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date without a year +#. and a time, in 12-hour format. +#: ../plugins/itip-formatter/itip-view.c:293 ../storage/sunone-itip-view.c:249 +msgid "%A, %B %e %l:%M:%S %p" +msgstr "%A, %B %e %l:%M:%S %p" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday and a date. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday and a date. +#: ../plugins/itip-formatter/itip-view.c:299 ../storage/sunone-itip-view.c:255 +msgid "%A, %B %e, %Y" +msgstr "%A, %B %e, %Y" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 24-hour format, without seconds. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 24-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:305 +#: ../plugins/itip-formatter/itip-view.c:304 ../storage/sunone-itip-view.c:260 +msgid "%A, %B %e, %Y %H:%M" +msgstr "%A, %B %e, %Y %H:%M" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 24-hour format. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 24-hour format. +#: ../plugins/itip-formatter/itip-view.c:309 +#: ../plugins/itip-formatter/itip-view.c:308 ../storage/sunone-itip-view.c:264 +msgid "%A, %B %e, %Y %H:%M:%S" +msgstr "%A, %B %e, %Y %H:%M:%S" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 12-hour format, without seconds. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 12-hour format, without seconds. +#: ../plugins/itip-formatter/itip-view.c:314 +#: ../plugins/itip-formatter/itip-view.c:313 ../storage/sunone-itip-view.c:269 +msgid "%A, %B %e, %Y %l:%M %p" +msgstr "%A, %B %e, %Y %l:%M %p" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 12-hour format. +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. strftime format of a weekday, a date and a +#. time, in 12-hour format. +#: ../plugins/itip-formatter/itip-view.c:318 +#: ../plugins/itip-formatter/itip-view.c:317 ../storage/sunone-itip-view.c:273 +msgid "%A, %B %e, %Y %l:%M:%S %p" +msgstr "%A, %B %e, %Y %l:%M:%S %p" + +#: ../plugins/itip-formatter/itip-view.c:343 +#: ../plugins/itip-formatter/itip-view.c:342 ../storage/sunone-itip-view.c:299 +#, c-format +msgid "%s through %s has published the following meeting information:" +msgstr "%s thông qua « %s » đã công bố tin tức cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:345 +#: ../plugins/itip-formatter/itip-view.c:344 ../storage/sunone-itip-view.c:301 +#, c-format +msgid "%s has published the following meeting information:" +msgstr "%s đã công bố tin tức cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:350 +#: ../plugins/itip-formatter/itip-view.c:349 ../storage/sunone-itip-view.c:306 +#, c-format +msgid "%s has delegated the following meeting to you:" +msgstr "%s đã ủy nhiệm cuộc họp này cho bạn:" + +#: ../plugins/itip-formatter/itip-view.c:353 +#: ../plugins/itip-formatter/itip-view.c:352 ../storage/sunone-itip-view.c:309 +#, fuzzy, c-format +msgid "%s through %s requests your presence at the following meeting:" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"%s thông qua « %s » yêu cầu sự hiện diện của bạn tại cuộc họp này:\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"%s thông qua %s yêu cầu sự hiện diện của bạn tại cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:355 +#: ../plugins/itip-formatter/itip-view.c:354 ../storage/sunone-itip-view.c:311 +#, c-format +msgid "%s requests your presence at the following meeting:" +msgstr "%s yêu cầu sự hiện diện của bạn tại cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:361 +#: ../plugins/itip-formatter/itip-view.c:360 ../storage/sunone-itip-view.c:320 +#, fuzzy, c-format +msgid "%s through %s wishes to add to an existing meeting:" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"%s thông qua « %s » muốn thêm vào một cuộc họp đã có :\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"%s thông qua %s muốn thêm vào một cuộc họp đã có :" + +#: ../plugins/itip-formatter/itip-view.c:363 +#: ../plugins/itip-formatter/itip-view.c:362 ../storage/sunone-itip-view.c:322 +#, c-format +msgid "%s wishes to add to an existing meeting:" +msgstr "%s muốn thêm vào một cuộc họp đã có :" + +#: ../plugins/itip-formatter/itip-view.c:366 +#: ../plugins/itip-formatter/itip-view.c:365 ../storage/sunone-itip-view.c:325 +#, c-format +msgid "" +"%s wishes to receive the latest information for the following meeting:" +msgstr "%s muốn nhận tin tức về cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:369 +#: ../plugins/itip-formatter/itip-view.c:368 ../storage/sunone-itip-view.c:328 +#, c-format +msgid "%s has sent back the following meeting response:" +msgstr "%s đã trả lời về cuộc họp:" + +#: ../plugins/itip-formatter/itip-view.c:373 +#: ../plugins/itip-formatter/itip-view.c:372 ../storage/sunone-itip-view.c:332 +#, fuzzy, c-format +msgid "%s through %s has cancelled the following meeting:" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"%s thông qua « %s » đã hủy bỏ cuộc họp này:\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"%s thông qua %s đã hủy bỏ cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:375 +#: ../plugins/itip-formatter/itip-view.c:374 ../storage/sunone-itip-view.c:334 +#, c-format +msgid "%s has cancelled the following meeting." +msgstr "%s đã hủy bỏ cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:378 +#: ../plugins/itip-formatter/itip-view.c:377 ../storage/sunone-itip-view.c:337 +#, c-format +msgid "%s has proposed the following meeting changes." +msgstr "%s đã đệ nghị những thay đổi cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:382 +#: ../plugins/itip-formatter/itip-view.c:381 ../storage/sunone-itip-view.c:341 +#, fuzzy, c-format +msgid "%s through %s has declined the following meeting changes:" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"%s thông qua « %s » đã từ chối những thay đổi cuộc họp này:\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"%s thông qua %s đã từ chối những thay đổi cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:384 +#: ../plugins/itip-formatter/itip-view.c:383 ../storage/sunone-itip-view.c:343 +#, c-format +msgid "%s has declined the following meeting changes." +msgstr "%s đã từ chối những thay đổi cuộc họp này:" + +#: ../plugins/itip-formatter/itip-view.c:411 +#: ../plugins/itip-formatter/itip-view.c:410 ../storage/sunone-itip-view.c:370 +#, c-format +msgid "%s through %s has published the following task:" +msgstr "%s thông qua « %s » đã công bố tác vụ này:" + +#: ../plugins/itip-formatter/itip-view.c:413 +#: ../plugins/itip-formatter/itip-view.c:412 ../storage/sunone-itip-view.c:372 +#, c-format +msgid "%s has published the following task:" +msgstr "%s đã công bố tác vụ này:" + +#: ../plugins/itip-formatter/itip-view.c:418 +#: ../plugins/itip-formatter/itip-view.c:417 ../storage/sunone-itip-view.c:377 +#, c-format +msgid "%s requests the assignment of %s to the following task:" +msgstr "%s yêu cầu gán %s cho tác vụ này:" + +#: ../plugins/itip-formatter/itip-view.c:421 +#: ../plugins/itip-formatter/itip-view.c:420 ../storage/sunone-itip-view.c:380 +#, c-format +msgid "%s through %s has assigned you a task:" +msgstr "%s thông qua %s đã gán bạn cho tác vụ này:" + +#: ../plugins/itip-formatter/itip-view.c:423 +#: ../plugins/itip-formatter/itip-view.c:422 ../storage/sunone-itip-view.c:382 +#, c-format +msgid "%s has assigned you a task:" +msgstr "%s đã gán bạn cho tác vụ này:" + +#: ../plugins/itip-formatter/itip-view.c:429 +#: ../plugins/itip-formatter/itip-view.c:428 ../storage/sunone-itip-view.c:388 +#, c-format +msgid "%s through %s wishes to add to an existing task:" +msgstr "%s thông qua « %s » muốn thêm vào tác vụ đã có :" + +#: ../plugins/itip-formatter/itip-view.c:431 +#: ../plugins/itip-formatter/itip-view.c:430 ../storage/sunone-itip-view.c:390 +#, c-format +msgid "%s wishes to add to an existing task:" +msgstr "%s muốn thêm vào tác vụ đã có :" + +#: ../plugins/itip-formatter/itip-view.c:434 +#: ../plugins/itip-formatter/itip-view.c:433 ../storage/sunone-itip-view.c:393 +#, c-format +msgid "" +"%s wishes to receive the latest information for the following " +"assigned task:" +msgstr "%s muốn nhận tin tức về tác vụ đã gán này:" + +#: ../plugins/itip-formatter/itip-view.c:437 +#: ../plugins/itip-formatter/itip-view.c:436 ../storage/sunone-itip-view.c:396 +#, c-format +msgid "%s has sent back the following assigned task response:" +msgstr "%s đã trả lời tác vụ đã gán:" + +#: ../plugins/itip-formatter/itip-view.c:441 +#: ../plugins/itip-formatter/itip-view.c:440 ../storage/sunone-itip-view.c:400 +#, c-format +msgid "%s through %s has cancelled the following assigned task:" +msgstr "%s thông qua « %s » đã hủy bỏ tác vụ đã gán này:" + +#: ../plugins/itip-formatter/itip-view.c:443 +#: ../plugins/itip-formatter/itip-view.c:442 ../storage/sunone-itip-view.c:402 +#, c-format +msgid "%s has cancelled the following assigned task:" +msgstr "%s đã hủy bỏ tác vụ đã gán này:" + +#: ../plugins/itip-formatter/itip-view.c:446 +#: ../plugins/itip-formatter/itip-view.c:445 ../storage/sunone-itip-view.c:405 +#, c-format +msgid "%s has proposed the following task assignment changes:" +msgstr "%s đã đệ nghị những thay đổi cách gán tác vụ này:" + +#: ../plugins/itip-formatter/itip-view.c:450 +#: ../plugins/itip-formatter/itip-view.c:449 ../storage/sunone-itip-view.c:409 +#, c-format +msgid "%s through %s has declined the following assigned task:" +msgstr "%s thông qua %s đã từ chối tác vụ đã gán này:" + +#: ../plugins/itip-formatter/itip-view.c:452 +#: ../plugins/itip-formatter/itip-view.c:451 ../storage/sunone-itip-view.c:411 +#, c-format +msgid "%s has declined the following assigned task:" +msgstr "%s đã từ chối tác vụ đã gán này:" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. Start time +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. Start time +#: ../plugins/itip-formatter/itip-view.c:891 +#: ../plugins/itip-formatter/itip-view.c:890 ../storage/sunone-itip-view.c:735 +#, fuzzy +msgid "Start time:" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Thời điểm đầu :\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"Giờ đầu :" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. End time +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. End time +#: ../plugins/itip-formatter/itip-view.c:900 +#: ../plugins/itip-formatter/itip-view.c:899 ../storage/sunone-itip-view.c:744 +#, fuzzy +msgid "End time:" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Thời điểm cuối:\n" +"#-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-#\n" +"Giờ cuối:" + +#. #-#-#-#-# evolution-jescs.vi.po (evolution-jescs HEAD) #-#-#-#-# +#. Comment +#: ../plug-ins/common/xbm.c:1213 ../src/dialogs.c:322 +#: ogg123/oggvorbis_format.c:57 ogg123/oggvorbis_format.c:58 +#: ../objects/UML/class_dialog.c:308 ../objects/UML/class_dialog.c:997 +#: ../objects/UML/class_dialog.c:2057 ../objects/UML/class_dialog.c:2188 +#: ../storage/sunone-itip-view.c:760 ../storage/sunone-itip-view.c:805 +msgid "Comment:" +msgstr "Ghi chú :" + +#: ../plugins/itip-formatter/itip-view.c:980 +#: ../plugins/itip-formatter/itip-view.c:979 +msgid "Send u_pdates to attendees" +msgstr "Gởi thông báo cập nhật cho các người dự" + +#: ../plugins/itip-formatter/itip-view.c:989 +#: ../plugins/itip-formatter/itip-view.c:988 +msgid "A_pply to all instances" +msgstr "Á_p dụng vào mọi lần" + +#: ../plugins/itip-formatter/org-gnome-itip-formatter.eplug.xml.h:1 +msgid "Displays text/calendar parts in messages." +msgstr "Hiển thị phần văn bản/lịch trong thư." + +#: ../plugins/itip-formatter/org-gnome-itip-formatter.eplug.xml.h:2 +msgid "Itip Formatter" +msgstr "Bộ định dạng Itip" + +#: ../plugins/itip-formatter/org-gnome-itip-formatter.error.xml.h:1 +msgid "" +""{0}" has delegated the meeting. Do you want to add the delegate " +""{1}" ?" +msgstr "" +"« {0} » đã ủy nhiệm cuộc họp này. Bạn có muốn thêm người ủy nhiệm « {1} » " +"không?" + +#: ../plugins/itip-formatter/org-gnome-itip-formatter.error.xml.h:3 +msgid "This meeting has been delegated" +msgstr "Cuộc họp này đã được ủy nhiệm." + +#: ../plugins/itip-formatter/org-gnome-itip-formatter.error.xml.h:4 +msgid "" +"This response is not from a current attendee. Add the sender as an attendee?" +msgstr "" +"Hồi đáp này không phải đến từ một người dự hiện thời. Thêm người này như là " +"người dự không?" + +#: ../plugins/mail-account-disable/mail-account-disable.c:47 +msgid "Proxy _Logout" +msgstr "Đăng _xuất ủy nhiệm" + +#: ../plugins/mail-account-disable/org-gnome-mail-account-disable.eplug.xml.h:1 +msgid "Allows disabling of accounts." +msgstr "Cho phép vô hiệu hóa tài khoản" + +#: ../plugins/mail-account-disable/org-gnome-mail-account-disable.eplug.xml.h:2 +msgid "Disable Account" +msgstr "Vô hiệu hóa tài khoản" + +#: ../plugins/mail-remote/client.c:30 +#, c-format +msgid "System error: %s" +msgstr "Lỗi hệ thống: %s" + +#: ../plugins/mail-remote/client.c:32 +#, c-format +msgid "Camel error: %s" +msgstr "Lỗi Camel: %s" + +#: ../plugins/mail-remote/evolution-mail-store.c:476 +msgid "Account cannot send e-mail" +msgstr "Tài khoản không gởi được thư điện tử." + +#: ../plugins/mail-remote/evolution-mail-store.c:605 +msgid "No store available" +msgstr "Không có kho." + +#: ../plugins/mail-remote/org-gnome-evolution-mail-remote.eplug.xml.h:1 +msgid "" +"A plugin which implements a CORBA interface for accessing mail data remotely." +msgstr "Bộ cầm phít thực hiện giao diện CORBA để truy cập dữ liệu thư từ xa." + +#: ../plugins/mail-remote/org-gnome-evolution-mail-remote.eplug.xml.h:2 +msgid "Mail Remote" +msgstr "Thư từ xa" + +#: ../plugins/mail-to-meeting/org-gnome-mail-to-meeting.eplug.xml.h:1 +msgid "" +"A plugin which allows the creation of meetings from the contents of a mail " +"message." +msgstr "" +"Một trình cầm phít cho phép tạo cuộc họp từ nội dung của một thư nào đó." + +#: ../plugins/mail-to-meeting/org-gnome-mail-to-meeting.eplug.xml.h:2 +msgid "Con_vert to Meeting" +msgstr "_Chuyển đổi sang cuộc họp" + +#: ../plugins/mail-to-meeting/org-gnome-mail-to-meeting.eplug.xml.h:3 +msgid "Mail to meeting" +msgstr "Gởi thư chơ cuộc họp" + +#: ../plugins/mail-to-task/org-gnome-mail-to-task.eplug.xml.h:1 +msgid "" +"A plugin which allows the creation of tasks from the contents of a mail " +"message." +msgstr "Bộ cầm phít cho phép tạo tác vụ từ nội dung thư." + +#: ../plugins/mail-to-task/org-gnome-mail-to-task.eplug.xml.h:2 +msgid "Con_vert to Task" +msgstr "_Chuyển đổi sang Tác vụ" + +#: ../plugins/mail-to-task/org-gnome-mail-to-task.eplug.xml.h:3 +msgid "Mail to task" +msgstr "Gởi thư chơ tác vụ" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:1 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:1 +msgid "Contact list _owner" +msgstr "Liên lạc với người _chủ hộp" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:2 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:5 +msgid "Get list _archive" +msgstr "Gọi _kho hộp" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:3 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:6 +msgid "Get list _usage information" +msgstr "Gọi thông tin _dùng hộp" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:4 +msgid "Mailing List Actions" +msgstr "Hành động hôp thư chung" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:5 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:7 +msgid "Mailing _List" +msgstr "_Hôp thư chung" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:6 +msgid "" +"Provide actions for common mailing list commands (subscribe, " +"unsubscribe, ...)." +msgstr "" +"Cung cấp hành động cho lệnh hộp thư chung thường (đăng ký, bỏ đăng ký ...)" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:7 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:11 +msgid "_Post message to list" +msgstr "_Gởi thư cho hộp" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:8 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:12 +msgid "_Subscribe to list" +msgstr "Đăng _ký với hộp" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.eplug.xml.h:9 +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:13 +msgid "_Un-subscribe to list" +msgstr "_Bỏ đăng ký với hộp" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:1 +msgid "Action not available" +msgstr "Hành động không sẵn sàng" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:2 +msgid "" +"An e-mail message will be sent to the URL \"{0}\". You can either send the " +"message automatically, or see and change it first.\n" +"\n" +"You should receive an answer from the mailing list shortly after the message " +"has been sent." +msgstr "" +"Một thư điện tử sẽ được gởi cho địa chỉ Mạng « {0} ». Bạn có thể hoặc tự " +"động gởi thư đó, hoặc xem và sửa đổi nó trước tiên.\n" +"\n" +"Bạn nên nhận một trả lời từ hộp thư chung một chút sau khi gởi thư đó." + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:5 +msgid "Malformed header" +msgstr "Dòng đầu sai dạng thức" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:6 +msgid "No e-mail action" +msgstr "Không có hành động thư" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:7 +msgid "Posting not allowed" +msgstr "Không cho phép gởi thư" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:8 +msgid "" +"Posting to this mailing list is not allowed. Possibly, this is a read-only " +"mailing list. Contact the list owner for details." +msgstr "" +"Không cho phép gởi thư cho hộp thư chung này. Có lẽ nó là hộp thư chung chỉ " +"cho phép đọc. Hãy liên lạc với người chủ hộp thư chung, để tìm biết chi tiết." + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:9 +msgid "Send e-mail message to mailing list?" +msgstr "Gởi thư cho hộp thư chung không?" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:10 +msgid "" +"The action could not be performed. This means the header for this action did " +"not contain any action we could handle.\n" +"\n" +"Header: {0}" +msgstr "" +"Không thực hiện được hành động đó. Có nghĩa là dòng đầu của hành động này " +"không chứa hành động nào trình này có quản lý được.\n" +"\n" +"Dòng đầu : « {0} »" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:13 +msgid "" +"The {0} header of this message is malformed and could not be processed.\n" +"\n" +"Header: {1}" +msgstr "" +"Dòng đầu « {0} » của thư này có dạng sai nên không xử lý được nó.\n" +"\n" +"Dòng đầu : « {1} »" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:16 +msgid "" +"This message does not contain the header information required for this " +"action." +msgstr "Thư này không chứa thông tin đầu thư cần thiết cho hành động này." + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:17 +msgid "_Edit message" +msgstr "_Sửa đổi thư" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.error.xml.h:18 +msgid "_Send message" +msgstr "_Gởi thư" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:2 +msgid "Contact the owner of the mailing list this message belongs to" +msgstr "Liên lạc với người chủ hộp thư chung của thư này." + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:3 +msgid "Get an archive of the list this message belongs to" +msgstr "Gọi kho của hộp thư chung của thư này" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:4 +msgid "Get information about the usage of the list this message belongs to" +msgstr "Gọi thông tin về cách sử dụng hộp thư chung của thư này" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:8 +msgid "Post a message to the mailing list this message belongs to" +msgstr "Gởi thư cho hộp thư chung của thư này" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:9 +msgid "Subscribe to the mailing list this message belongs to" +msgstr "Đăng ký với hộp thư chung của thư này" + +#: ../plugins/mailing-list-actions/org-gnome-mailing-list-actions.xml.h:10 +msgid "Unsubscribe to the mailing list this message belongs to" +msgstr "Bỏ đăng ký với hộp thư chung của thư này" + +#: ../plugins/mark-calendar-offline/org-gnome-mark-calendar-offline.eplug.xml.h:1 +msgid "Mark calendar offline" +msgstr "Nhãn lịch này ngoại tuyến" + +#: ../plugins/mark-calendar-offline/org-gnome-mark-calendar-offline.eplug.xml.h:2 +msgid "Marks the selected calendar for offline viewing." +msgstr "Đánh dấu lịch đã chọn để xem khi ngoại tuyến." + +#: ../plugins/mark-calendar-offline/org-gnome-mark-calendar-offline.eplug.xml.h:3 +msgid "_Do not make this available offline" +msgstr "_Không cho phép điều này sẵn sàng ngoại tuyến" + +#: ../plugins/mark-calendar-offline/org-gnome-mark-calendar-offline.eplug.xml.h:4 +msgid "_Mark Calendar for offline use" +msgstr "_Nhãn lịch để dùng ngoại tuyến" + +#: ../plugins/mono/org-gnome-evolution-mono.eplug.xml.h:1 +msgid "A plugin which implements mono plugins." +msgstr "Bộ cầm phít thực hiện bộ cầm phít một nguồn." + +#: ../plugins/mono/org-gnome-evolution-mono.eplug.xml.h:2 +msgid "Mono Loader" +msgstr "Bộ tải điều một nguồn" + +#: ../plugins/new-mail-notify/org-gnome-new-mail-notify.eplug.xml.h:1 +msgid "Generates a D-BUS message when new mail arrives." +msgstr "Tạo ra một thông điệp D-BUS khi nhận thư mới." + +#: ../plugins/new-mail-notify/org-gnome-new-mail-notify.eplug.xml.h:2 +msgid "New Mail Notification" +msgstr "Thông báo Thư Mới" + +#: ../plugins/new-mail-notify/org-gnome-new-mail-notify.eplug.xml.h:3 +msgid "New mail notify" +msgstr "Thông báo Thư Mới" + +#: ../plugins/plugin-manager/org-gnome-plugin-manager.eplug.xml.h:1 +msgid "A plugin for managing which plugins are enabled or disabled." +msgstr "Một trình cầm phít quản lý trình cầm phít nào bật hay tắt." + +#: ../plugins/plugin-manager/org-gnome-plugin-manager.eplug.xml.h:2 +msgid "Plugin manager" +msgstr "Bộ quản lý trình cầm phít" + +#: ../plugins/plugin-manager/org-gnome-plugin-manager.xml.h:1 +msgid "Enable and disable plugins" +msgstr "Bật và tắt trình cầm phít" + +#: ../testing/test-handlers.c:482 +msgid "Plugins" +msgstr "Bộ cầm phít" + +#: ../plugins/plugin-manager/plugin-manager.c:45 +msgid "Author(s)" +msgstr "Tác giả" + +#: ../providers/odbc/gda-odbc-provider.c:1162 +msgid "Id" +msgstr "ID" + +#: ../app/vectors/gimpvectors.c:229 +msgid "Path" +msgstr "Đường dẫn" + +#: ../gnomeofficeui/go-plugin-manager-dialog.c:220 +msgid "Plugin Manager" +msgstr "Bộ quản lý trình cầm phít" + +#: ../plugins/plugin-manager/plugin-manager.c:201 +msgid "Note: Some changes will not take effect until restart" +msgstr "" +"Ghi chú : một số thay đổi sẽ không hoạt động cho đến khi đã khởi động lại" + +#: ../gedit/dialogs/gedit-plugin-manager.c:55 +msgid "Plugin" +msgstr "Bộ cầm phít" + +#: ../plugins/prefer-plain/org-gnome-prefer-plain.eplug.xml.h:1 +msgid "" +"A test plugin which demonstrates a formatter plugin which lets you choose to " +"disable HTML mails.\n" +"\n" +"This plugin is unsupported demonstration code only.\n" +msgstr "" +"Một trình cầm phít thừ ra mà biểu diễn một trình cầm phít định dạng cho phép " +"bạn chọn tắt thư HTML.\n" +"\n" +"Trình cầm phít này chỉ chứa mã biểu diễn không được hỗ trợ thôi.\n" + +#. but then we also need to create our own section frame +#: ../plugins/prefer-plain/org-gnome-prefer-plain.eplug.xml.h:6 +msgid "Plain Text Mode" +msgstr "Chế độ chữ thô" + +#: ../plugins/prefer-plain/org-gnome-prefer-plain.eplug.xml.h:7 +msgid "Prefer plain-text" +msgstr "Thích chữ thô hơn" + +#: ../plugins/prefer-plain/prefer-plain.c:105 +msgid "Show HTML if present" +msgstr "Hiển thị HTML nếu có" + +#: ../plugins/prefer-plain/prefer-plain.c:106 +msgid "Prefer PLAIN" +msgstr "Thích chữ thô hơn" + +#: ../plugins/prefer-plain/prefer-plain.c:107 +msgid "Only ever show PLAIN" +msgstr "Chỉ hiển thị chữ thô" + +#: ../plugins/prefer-plain/prefer-plain.c:150 +msgid "HTML Mode" +msgstr "Chế độ HTML" + +#: ../plugins/print-message/org-gnome-print-message.eplug.xml.h:1 +msgid "Gives an option to print mail from composer" +msgstr "Cung cấp tùy chọn để in thư từ bộ soạn" + +#: ../gtk/gtkstock.c:393 +msgid "Print Pre_view" +msgstr "_Xem thử bản in" + +#: ../plugins/print-message/org-gnome-print-message.xml.h:2 +msgid "Prints the message" +msgstr "In thư này" + +#: ../plugins/sa-junk-plugin/em-junk-filter.c:97 +msgid "Spamassassin (built-in)" +msgstr "Spamassassin (sẵn có)" + +#: ../plugins/sa-junk-plugin/org-gnome-sa-junk-plugin.eplug.xml.h:1 +msgid "Sa junk-plugin" +msgstr "Bộ cầm phít Thư rác SA" + +#: ../plugins/sa-junk-plugin/org-gnome-sa-junk-plugin.eplug.xml.h:2 +msgid "learns junk messages using spamd." +msgstr "học biết phát hiện thư rác, dùng trình nền spamd" + +#: ../plugins/save-attachments/org-gnome-save-attachments.eplug.xml.h:1 +msgid "A plugin for saving all attachments or parts of a message at once." +msgstr "Một trình cầm phít lưu mọi đính kèm hay phần thư đều cùng lúc." + +#: ../plugins/save-attachments/org-gnome-save-attachments.eplug.xml.h:2 +msgid "Save attachments" +msgstr "Lưu đính kèm" + +#: ../plugins/save-attachments/org-gnome-save-attachments.xml.h:1 +msgid "Save Attachments ..." +msgstr "Lưu các đính kèm..." + +#: ../plugins/save-attachments/org-gnome-save-attachments.xml.h:2 +msgid "Save all attachments" +msgstr "Lưu mọi đính kèm" + +#: ../plugins/save-attachments/save-attachments.c:338 +#: ../plugins/save-attachments/save-attachments.c:331 +msgid "Select save base name" +msgstr "Chọn tên cơ bản khi lưu" + +#: ../plugins/save-attachments/save-attachments.c:358 +msgid "MIME Type" +msgstr "Kiểu MIME:" + +#: ../plugins/save-calendar/csv-format.c:171 +msgid "%F %T" +msgstr "%F %T" + +#: ../plugins/save-calendar/csv-format.c:385 +msgid "Uid" +msgstr "UID" + +#: ../plugins/save-calendar/csv-format.c:387 +msgid "Description List" +msgstr "Danh sách mô tả" + +#: ../plugins/save-calendar/csv-format.c:388 +msgid "Categories List" +msgstr "Danh sách phân loại" + +#: ../plugins/save-calendar/csv-format.c:389 +msgid "Comment List" +msgstr "Danh sách chú thích" + +#: ../plugins/save-calendar/csv-format.c:391 +#: ../mimedir/mimedir-vcomponent.c:438 +msgid "Created" +msgstr "Đã tạo" + +#: ../plugins/save-calendar/csv-format.c:392 +msgid "Contact List" +msgstr "Danh sách liên lạc" + +#: ../src/main-window.c:328 ../objects/FS/function.c:952 +#: ../widgets/gtk+.xml.in.h:170 app/envelope-box.c:1018 +#: app/sample-editor.c:261 +#, fuzzy +msgid "Start" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Bắt đầu\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Bắt đầu\n" +"#-#-#-#-# soundtracker-0.6.7.vi.po (soundtracker) #-#-#-#-#\n" +"Đầu" + +#: ../partman-partitioning.templates:97 ../widgets/gtk+.xml.in.h:60 +#: app/envelope-box.c:1019 app/sample-editor.c:262 +#, fuzzy +msgid "End" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Kết thúc\n" +"#-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-#\n" +"Kết thúc\n" +"#-#-#-#-# soundtracker-0.6.7.vi.po (soundtracker) #-#-#-#-#\n" +"Cuối" + +#: ../plugins/save-calendar/csv-format.c:396 +msgid "percent Done" +msgstr "Phần trăm xong" + +#: ../plugins/save-calendar/csv-format.c:398 +msgid "Url" +msgstr "Địa chỉ Mạng" + +#: ../plugins/save-calendar/csv-format.c:399 +msgid "Attendees List" +msgstr "Danh sách người dự" + +#: ../gtk/gtkfilechooserdefault.c:3803 +msgid "Modified" +msgstr "Đã sửa đổi" + +#: ../plugins/save-calendar/csv-format.c:552 +msgid "Advanced options for the CSV format" +msgstr "Tùy chọn cấp cao cho khuôn dạng CSV" + +#: ../plugins/save-calendar/csv-format.c:559 +msgid "Prepend a header" +msgstr "Thêm dòng đầu vào đầu" + +#: ../plugins/save-calendar/csv-format.c:568 +msgid "Value delimiter:" +msgstr "Điều định giới giá trị:" + +#: ../plugins/save-calendar/csv-format.c:574 +msgid "Record delimiter:" +msgstr "Điều định giới mục ghi:" + +#: ../plugins/save-calendar/csv-format.c:580 +msgid "Encapsulate values with:" +msgstr "Bao giá trị dùng:" + +#: ../plugins/save-calendar/csv-format.c:602 +msgid "Comma separated value format (.csv)" +msgstr "Khuôn dạng giá trị định giới bằng dấu phẩy (.csv)" + +#: ../plugins/save-calendar/org-gnome-save-calendar.eplug.xml.h:1 +msgid "Save Selected" +msgstr "Lưu các điều chọn" + +#: ../plugins/save-calendar/org-gnome-save-calendar.eplug.xml.h:2 +msgid "Save to _Disk" +msgstr "Lưu vào _đĩa" + +#: ../plugins/save-calendar/org-gnome-save-calendar.eplug.xml.h:3 +msgid "Saves selected calendar or tasks list to disk." +msgstr "Lưu các lịch hay tác vụ đều đã chọn vào đĩa." + +#: ../plugins/save-calendar/rdf-format.c:158 +msgid "%FT%T" +msgstr "%FT%T" + +#: ../plugins/save-calendar/rdf-format.c:396 +msgid "RDF format (.rdf)" +msgstr "Khuôn dạng RDF (.rdf)" + +#: ../plugins/save-calendar/save-calendar.c:181 +msgid "Select destination file" +msgstr "Chọn tập tin đích" + +#: ../plugins/select-one-source/org-gnome-select-one-source.eplug.xml.h:1 +msgid "Select one source" +msgstr "Chọn một nguồn" + +#: ../plugins/select-one-source/org-gnome-select-one-source.eplug.xml.h:2 +msgid "Selects a single calendar or task source for viewing." +msgstr "Chọn chỉ một lịch hay nguồn tác vụ riêng lẻ để xem thôi." + +#: ../plugins/select-one-source/org-gnome-select-one-source.eplug.xml.h:3 +msgid "_Show only this Calendar" +msgstr "_Hiện chỉ Lịch này" + +#: ../plugins/select-one-source/org-gnome-select-one-source.eplug.xml.h:4 +msgid "_Show only this Task List" +msgstr "_Hiện chỉ danh sách tác vụ này" + +#: ../plugins/startup-wizard/org-gnome-evolution-startup-wizard.eplug.xml.h:1 +msgid "Startup wizard" +msgstr "Phụ tá khởi động" + +#: ../plugins/startup-wizard/startup-wizard.c:85 +msgid "Evolution Setup Assistant" +msgstr "Trợ tá thiết lập Evolution" + +#: ../plugins/startup-wizard/startup-wizard.c:88 +#: ../storage/exchange-autoconfig-wizard.glade.h:13 ../src/wizard.glade.h:29 +msgid "Welcome" +msgstr "Chúc mừng bạn" + +#: ../plugins/startup-wizard/startup-wizard.c:89 +msgid "" +"Welcome to Evolution. The next few screens will allow Evolution to connect " +"to your email accounts, and to import files from other applications. \n" +"\n" +"Please click the \"Forward\" button to continue. " +msgstr "" +"Chào mừng bạn dùng Evolution. Những màn hình kế tiếp\n" +"sẽ cho phép Evolution kết nối với các tài khoản thư của bạn,\n" +"và để nhập các tập tin từ các ứng dụng khác.\n" +"\n" +"Vui lòng nhấn nút «Tiếp » để tiếp tục." + +#: ../plugins/startup-wizard/startup-wizard.c:140 +#: ../shell/e-shell-importer.c:147 ../shell/e-shell-importer.c:145 +msgid "Please select the information that you would like to import:" +msgstr "Hãy chọn thông tin bạn muốn nhập:" + +#: ../plugins/startup-wizard/startup-wizard.c:154 +#: ../shell/e-shell-importer.c:400 ../shell/e-shell-importer.c:398 +#, c-format +msgid "From %s:" +msgstr "Từ %s:" + +#: ../plugins/startup-wizard/startup-wizard.c:234 +#: ../shell/e-shell-importer.c:511 ../shell/e-shell-importer.c:509 +msgid "Importing data." +msgstr "Đang nhập dữ liệu." + +#: ../plugins/subject-thread/org-gnome-subject-thread.eplug.xml.h:1 +msgid "Indicates if threading of messages should fall back to subject." +msgstr "Ngụ ý nếu cách tạo mạch thư nên trở về theo chủ đề" + +#: ../plugins/subject-thread/org-gnome-subject-thread.eplug.xml.h:2 +msgid "Subject Threading" +msgstr "Tạo mạch theo chủ đề" + +#: ../plugins/subject-thread/org-gnome-subject-thread.eplug.xml.h:3 +msgid "Thread messages by subject" +msgstr "Hiển thị mạch trong danh sách thư, theo Chủ đề" + +#. Create the checkbox we will display, complete with mnemonic that is unique in the dialog +#: ../plugins/subject-thread/subject-thread.c:54 +msgid "Fall back to threading messages by sub_ject" +msgstr "Trở về tạo mạch thư theo _chủ đề" + +#: ../shell/GNOME_Evolution_Shell.server.in.in.h:1 +msgid "Evolution Shell" +msgstr "Hệ vỏ Evolution" + +#: ../shell/GNOME_Evolution_Shell.server.in.in.h:2 +msgid "Evolution Shell Config factory" +msgstr "Bộ tạo cấu hình hệ vỏ Evolution" + +#: ../shell/GNOME_Evolution_Test.server.in.in.h:1 +msgid "Evolution Test" +msgstr "Kiểm tra Evolution" + +#: ../shell/GNOME_Evolution_Test.server.in.in.h:2 +msgid "Evolution Test component" +msgstr "Thành phần kiểm tra Evolution" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:1 +msgid "A GNOME Print description of the current printer settings" +msgstr "Mô tả In GNOME của thiết lập máy in hiện có" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:2 +msgid "Configuration version" +msgstr "Phiên bản cấu hình" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:3 +msgid "Default sidebar width" +msgstr "Độ rộng thanh nách mặc định" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:4 +msgid "Default window height" +msgstr "Độ cao cửa sổ mặc định" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:5 +msgid "Default window width" +msgstr "Độ rộng cửa sổ mặc định" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:6 +msgid "ID or alias of the component to be shown by default at start-up." +msgstr "Mặc định là hiển thị ID hay biệt hiệu của thành phần khi khởi động." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:7 +msgid "Last upgraded configuration version" +msgstr "Phiên bản cấu hình Evolution đã cập nhật cuối cùng" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:8 +msgid "" +"List of paths for the folders to be synchronized to disk for offline usage" +msgstr "" +"Danh sách đường dẫn cho những thư mục sẽ được đồng bộ với đĩa để sử dụng " +"ngoại tuyến." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:9 +msgid "Printer settings" +msgstr "Thiết lập máy in" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:10 +msgid "Skip development warning dialog" +msgstr "Bỏ qua hộp thoại cảnh báo phát triển" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:11 ../shell/main.c:473 +#: ../shell/main.c:468 +msgid "Start in offline mode" +msgstr "Khởi chạy trong chế độ ngoại tuyến" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:12 +msgid "" +"The configuration version of Evolution, with major/minor/configuration level" +msgstr "Phiên bản cấu hình của trình Evolution, với mức độ cấu hình lớn/nhỏ" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:13 +msgid "The default height for the main window, in pixels." +msgstr "Độ cao mặc định cửa của sổ chính, theo điểm ảnh." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:14 +msgid "The default width for the main window, in pixels." +msgstr "Độ rộng mặc định cửa của sổ chính, theo điểm ảnh." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:15 +msgid "The default width for the sidebar, in pixels." +msgstr "Độ rộng mặc định của thanh nách, theo điểm ảnh." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:16 +msgid "" +"The last upgraded configuration version of Evolution, with major/minor/" +"configuration level" +msgstr "" +"Phiên bản cấu hình Evolution đã cập nhật cuối cùng, với mức độ cấu hình lớn/" +"nhỏ" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:17 +msgid "" +"The style of the window buttons. Can be \"text\", \"icons\", \"both\", " +"\"toolbar\". If \"toolbar\" is set, the style of the buttons is determined " +"by the GNOME toolbar setting." +msgstr "" +"Kiểu dáng mọi cái nút cửa sổ. Có thể là « chữ », « ảnh », « cả hai » hay « " +"thanh công cụ». Nếu lập « thanh công cụ » thì thiết lập thanh công cụ GNOME " +"sẽ quyết định kiểu dáng các cái nút này." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:18 +msgid "Toolbar is visible" +msgstr "Hiện thanh công cụ" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:19 +msgid "Whether Evolution will start up in offline mode instead of online mode." +msgstr "" +"Có nên khỏi chạy trình Evolution trong chế độ ngoại tuyến thay vào chế độ " +"trực tuyến hay không." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:20 +msgid "Whether the toolbar should be visible." +msgstr "Có nên hiển thị thanh công cụ hay không." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:21 +msgid "" +"Whether the warning dialog in development versions of Evolution is skipped." +msgstr "" +"Có nên bỏ qua hộp thoại cảnh báo trong phiên bản phát triển Evolution hay " +"không." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:22 +msgid "Whether the window buttons should be visible." +msgstr "Có nên hiển thị mọi cái nút trên cửa sổ hay không." + +#: ../shell/apps_evolution_shell.schemas.in.in.h:23 +msgid "Window button style" +msgstr "Kiểu nút cửa sổ" + +#: ../shell/apps_evolution_shell.schemas.in.in.h:24 +msgid "Window buttons are visible" +msgstr "Hiển thị nút cửa sổ" + +#: ../shell/e-active-connection-dialog.glade.h:1 +msgid "Active Connections" +msgstr "Kết nối hoạt động" + +#: ../shell/e-active-connection-dialog.glade.h:2 +msgid "Active Connections" +msgstr "Kết nối hoạt động" + +#: ../shell/e-active-connection-dialog.glade.h:3 +msgid "Click OK to close these connections and go offline" +msgstr "Nhấn « Được » để đóng những kết nối này và chuyển sang ngoại tuyến." + +#: ../shell/e-shell-folder-title-bar.c:586 +#: ../shell/e-shell-folder-title-bar.c:587 +msgid "(Untitled)" +msgstr "(Không tên)" + +#: ../shell/e-shell-importer.c:135 ../shell/e-shell-importer.c:133 +msgid "Choose the type of importer to run:" +msgstr "Chọn kiểu bộ nhập cần chạy:" + +#: ../shell/e-shell-importer.c:138 ../shell/e-shell-importer.c:136 +msgid "" +"Choose the file that you want to import into Evolution, and select what type " +"of file it is from the list.\n" +"\n" +"You can select \"Automatic\" if you do not know, and Evolution will attempt " +"to work it out." +msgstr "" +"Hãy chọn tập tin muốn nhập vào Evolution, và chọn kiểu tập tin từ danh sách " +"dưới đây.\n" +"\n" +"Bạn có thể chọn « Tự động » nếu bạn không biết, và Evolution sẽ thử tự tìm " +"cách hoạt động." + +#: ../shell/e-shell-importer.c:144 ../shell/e-shell-importer.c:142 +msgid "Choose the destination for this import" +msgstr "Hãy chọn nhập vào đích nào" + +#: ../shell/e-shell-importer.c:150 ../shell/e-shell-importer.c:148 +msgid "" +"Evolution checked for settings to import from the following\n" +"applications: Pine, Netscape, Elm, iCalendar. No importable\n" +"settings found. If you would like to\n" +"try again, please click the \"Back\" button.\n" +msgstr "" +"Trình Evolution đã kiểm tra có thiết lập để nhập từ\n" +"những ứng dụng theo đây: Pine, Netscape, Elm, iCalendar.\n" +"Chưa tìm thiết lập có thể nhập. Nếu bạn muốn thử lại,\n" +"hãy nhắp vào cái nút « Lùi ».\n" + +#: ../shell/e-shell-importer.c:285 ../shell/e-shell-importer.c:283 +msgid "F_ilename:" +msgstr "_T_ên tập tin:" + +#: ../shell/e-shell-importer.c:290 ../shell/e-shell-importer.c:288 +#: ../src/zenity.glade.h:15 +msgid "Select a file" +msgstr "Chọn tập tin" + +#: ../shell/e-shell-importer.c:302 ../shell/e-shell-importer.c:300 +msgid "File _type:" +msgstr "_Kiểu tập tin:" + +#: ../shell/e-shell-importer.c:338 ../shell/e-shell-importer.c:336 +msgid "Import data and settings from _older programs" +msgstr "_Nhập dữ liệu và thiết lập từ chương trình cũ" + +#: ../shell/e-shell-importer.c:341 ../shell/e-shell-importer.c:339 +msgid "Import a _single file" +msgstr "Nhập một _tập tin đơn" + +#: ../plug-ins/common/postscript.c:3032 ../src/ImportDialog.cs:40 +msgid "_Import" +msgstr "_Nhập" + +#: ../shell/e-shell-settings-dialog.c:318 +msgid "Evolution Settings" +msgstr "Thiết lập Evolution" + +#: ../shell/e-shell-utils.c:118 +msgid "No folder name specified." +msgstr "Chưa ghi rõ tên thư mục." + +#: ../shell/e-shell-utils.c:125 +msgid "Folder name cannot contain the Return character." +msgstr "Tên thư mục không thể chứa ký tự Return." + +#: ../shell/e-shell-utils.c:131 +msgid "Folder name cannot contain the character \"/\"." +msgstr "Tên thư mục không thể chứa ký tự sổ chéo « / »" + +#: ../shell/e-shell-utils.c:137 +msgid "Folder name cannot contain the character \"#\"." +msgstr "Tên thư mục không thể chứa ký tự dấu thăng « # »." + +#: ../shell/e-shell-utils.c:143 +msgid "'.' and '..' are reserved folder names." +msgstr "" +"Dấu chấm « . » và hai dấu chấm tiếp tực « .. » là hai tên thư mục đặc biệt, " +"được dành riêng." + +#: ../shell/e-shell-window-commands.c:71 ../shell/e-shell-window-commands.c:69 +msgid "The GNOME Pilot tools do not appear to be installed on this system." +msgstr "Công cụ GNOME Pilot có lẽ chưa được cài đặt trên hệ thống này." + +#: ../shell/e-shell-window-commands.c:79 ../shell/e-shell-window-commands.c:77 +#, c-format +msgid "Error executing %s." +msgstr "Gặp lỗi khi thực hiện « %s »." + +#: ../shell/e-shell-window-commands.c:128 +#: ../shell/e-shell-window-commands.c:126 +msgid "Bug buddy is not installed." +msgstr "Chưa cài đặt trình Bug Buddy (thông báo lỗi)." + +#: ../shell/e-shell-window-commands.c:136 +#: ../shell/e-shell-window-commands.c:134 +msgid "Bug buddy could not be run." +msgstr "Không thể chạy trình Bug buddy." + +#: ../shell/e-shell-window-commands.c:547 +#: ../shell/e-shell-window-commands.c:521 +msgid "Groupware Suite" +msgstr "Bộ phần mềm nhóm (Groupware)" + +#: ../shell/e-shell-window-commands.c:778 +#: ../shell/e-shell-window-commands.c:749 +msgid "_Work Online" +msgstr "_Trực tuyến" + +#: ../ui/evolution.xml.h:47 +msgid "_Work Offline" +msgstr "_Ngoại tuyến" + +#: ../shell/e-shell-window-commands.c:804 +#: ../shell/e-shell-window-commands.c:775 +msgid "Work Offline" +msgstr "Ngoại tuyến" + +#: ../shell/e-shell-window.c:343 +msgid "Evolution is currently online. Click on this button to work offline." +msgstr "" +"Evolution hiện thời đang trực tuyến. Nhấn nút này để chuyển sang ngoại tuyến." + +#: ../shell/e-shell-window.c:351 +msgid "Evolution is in the process of going offline." +msgstr "Evolution đang chuyển sang ngoại tuyến." + +#: ../shell/e-shell-window.c:358 +msgid "Evolution is currently offline. Click on this button to work online." +msgstr "Evolution đang ngoại tuyến. Nhấn nút này để chuyển sang trực tuyến." + +#: ../shell/e-shell-window.c:735 ../shell/e-shell-window.c:724 +#, c-format +msgid "Switch to %s" +msgstr "Chuyển sang « %s »" + +#: ../shell/e-shell.c:625 ../shell/e-shell.c:620 +msgid "Uknown system error." +msgstr "Gặp lỗi hệ thống lạ." + +# Variable and unit: do not translate/ biến và đơn vị: đừng dịch +#: ../shell/e-shell.c:823 ../shell/e-shell.c:824 ../shell/e-shell.c:822 +#, c-format +msgid "%ld KB" +msgstr "%ld KB" + +#: ../shell/e-shell.c:1257 ../shell/e-shell.c:1278 +msgid "Invalid arguments" +msgstr "Đối số không hợp lệ" + +#: ../shell/e-shell.c:1259 ../shell/e-shell.c:1280 +msgid "Cannot register on OAF" +msgstr "Không thể đăng ký với OAF" + +#: ../shell/e-shell.c:1261 ../shell/e-shell.c:1282 +msgid "Configuration Database not found" +msgstr "Không tìm thấy cơ sở dữ liệu cấu hình" + +#: ../shell/evolution-test-component.c:140 +msgid "New Test" +msgstr "Kiểm tra mới" + +#: ../plug-ins/script-fu/script-fu.c:282 +msgid "_Test" +msgstr "_Thử ra" + +#: ../shell/evolution-test-component.c:142 +msgid "Create a new test item" +msgstr "Tạo mục kiểm tra mới" + +#: ../shell/import.glade.h:1 +msgid "Click \"Import\" to begin importing the file into Evolution. " +msgstr "Nhấn « Nhập » để bắt đầu nhập tập tin đó vào Evolution." + +#: ../shell/import.glade.h:2 +msgid "Evolution Import Assistant" +msgstr "Trợ tá nhập Evolution" + +#: ../shell/import.glade.h:3 +msgid "Import File" +msgstr "Nhập tập tin" + +#: ../shell/import.glade.h:4 +msgid "Import Location" +msgstr "Địa điểm nhập" + +#: ../shell/import.glade.h:5 +msgid "Importer Type" +msgstr "Loại bộ nhập" + +#: ../shell/import.glade.h:6 +msgid "Select Importers" +msgstr "Chọn bộ nhập" + +#: ../shell/import.glade.h:7 +msgid "Select a File" +msgstr "Chọn tập tin" + +#: ../shell/import.glade.h:8 +msgid "" +"Welcome to the Evolution Import Assistant.\n" +"With this assistant you will be guided through the process of\n" +"importing external files into Evolution." +msgstr "" +"Chào mừng dùng Trợ tá nhập Evolution.\n" +"Với trợ tá này, bạn sẽ được hướng dẫn thông qua tiến trình\n" +"nhập các tập tin bên ngoài vào Evolution." + +#. Preview/Alpha/Beta version warning message +#: ../shell/main.c:230 +#, no-c-format +msgid "" +"Hi. Thanks for taking the time to download this preview release\n" +"of the Evolution groupware suite.\n" +"\n" +"This version of Evolution is not yet complete. It is getting close,\n" +"but some features are either unfinished or do not work properly.\n" +"\n" +"If you want a stable version of Evolution, we urge you to uninstall\n" +"this version, and install version %s instead.\n" +"\n" +"If you find bugs, please report them to us at bugzilla.gnome.org.\n" +"This product comes with no warranty and is not intended for\n" +"individuals prone to violent fits of anger.\n" +"\n" +"We hope that you enjoy the results of our hard work, and we\n" +"eagerly await your contributions!\n" +msgstr "" +"Xin chào. Xin cám ơn đã mất thời gian để tải về bản dùng thử này\n" +"của bộ phần mềm nhóm Evolution.\n" +"\n" +"Đây là phiên bản Evolution chưa hoàn chỉnh. Nó gần hoàn chỉnh,\n" +"nhưng vẫn còn vài tính năng hoặc chưa hoàn chỉnh,\n" +"hoặc chưa làm việc đúng.\n" +"\n" +"Nếu bạn muốn dùng một phiên bản ổn định của Evolution, chúng tôi thúc giục " +"bạn bỏ cài đặt phiên bản này, và để cài đặt phiên bản %s thay vào đó.\n" +"\n" +"Nếu bạn tìm thấy lỗi, vui lòng thông báo cho chúng tôi tại .\n" +"Sản phầm này không bảo đảm gì cả.\n" +"\n" +"Chúng tôi hy vọng bạn thích kết quả của quá trình làm việc của chúng tôi,\n" +"và chúng tôi háo hức chờ đời sự đóng góp của bạn!\n" + +#: ../shell/main.c:254 +msgid "" +"Thanks\n" +"The Evolution Team\n" +msgstr "" +"Xin cám ơn\n" +"Nhóm Evolution\n" + +#: ../shell/main.c:261 +msgid "Don't tell me again" +msgstr "Đừng nói điều này lần nữa" + +#: ../shell/main.c:471 ../shell/main.c:466 +msgid "Start Evolution activating the specified component" +msgstr "Báo trình Evolution hoạt hóa thành phần đã ghi rõ" + +#: ../shell/main.c:475 ../shell/main.c:470 +msgid "Start in online mode" +msgstr "Khởi chạy trong chế độ trực tuyến" + +#: ../shell/main.c:478 ../shell/main.c:473 +msgid "Forcibly shut down all Evolution components" +msgstr "Buộc kết thúc mọi thành phần Evolution" + +#: ../shell/main.c:482 ../shell/main.c:477 +msgid "Forcibly re-migrate from Evolution 1.4" +msgstr "Buộc tái nâng cấp từ Evolution 1.4" + +#: ../shell/main.c:485 ../shell/main.c:480 +msgid "Send the debugging output of all components to a file." +msgstr "Gởi thông tin gỡ lỗi của mọi thành phần vào tập tin." + +#: ../shell/main.c:487 ../shell/main.c:482 +msgid "Disable loading of any plugins." +msgstr "Tắt tải trình cầm phít nào." + +#: ../shell/main.c:518 ../shell/main.c:513 +#, c-format +msgid "" +"%s: --online and --offline cannot be used together.\n" +" Use %s --help for more information.\n" +msgstr "" +"%s: hai tùy chọn « --online » (trực tuyến) và « --offline » (ngoại tuyến)\n" +"thì không thể được dùng chung.\n" +" Hãy dùng lệnh « %s --help » (trợ giúp) để biết thêm thông tin.\n" + +#: ../shell/shell.error.xml.h:1 +msgid "Are you sure you want to forget all remembered passwords?" +msgstr "Bạn có chắc muốn quên các mật khẩu đã nhớ không?" + +#: ../shell/shell.error.xml.h:3 +msgid "Delete old data from version {0}?" +msgstr "Xoá bỏ dữ liệu cũ từ phiên bản {0} không?" + +#: ../shell/shell.error.xml.h:4 +msgid "Evolution can not start." +msgstr "Evolution không khởi chạy được." + +#: ../shell/shell.error.xml.h:5 +msgid "" +"Forgetting your passwords will clear all remembered passwords. You will be " +"reprompted next time they are needed. " +msgstr "" +"Quên đi các mật khẩu đã nhớ sẽ xoá hết mật khẩu đã nhớ,. Như vậy bạn sẽ lại " +"được nhắc nhập mật khẩu lần sau cần thiết." + +#: ../shell/shell.error.xml.h:7 +msgid "Insufficient disk space for upgrade." +msgstr "Không có đủ sức chứa trên đĩa để nâng cấp." + +#: ../shell/shell.error.xml.h:8 +msgid "Really delete old data?" +msgstr "Bạn thật sự muốn xoá bỏ dữ liệu cũ không?" + +#: ../shell/shell.error.xml.h:9 +msgid "" +"The entire contents of the "evolution" directory is about to be be " +"permanently removed.\n" +"\n" +"It is suggested you manually verify that all of your mail, contact, and " +"calendar data is present, and that this version of Evolution operates " +"correctly before deleting this old data.\n" +"\n" +"Once deleted, you cannot downgrade to the previous version of Evolution " +"without manual intervention.\n" +msgstr "" +"Sắp gỡ bỏ hoàn toàn toàn nội dung của thư mục « evolution».\n" +"\n" +"Có đề nghị là bạn tự kiểm chứng có tất cả dữ liệu thư, liên lạc và lịch " +"trong phiên bản mới, mà hoặt động cho đúng, trước khi xoá bỏ dữ liệu cũ " +"này.\n" +"\n" +"Một khi đã xoá bỏ nó, không thể trở lại « xuống » phiên bản trước nếu không " +"có khả năng đặc biệt cấp cao.\n" + +#: ../shell/shell.error.xml.h:15 +msgid "" +"The previous version of evolution stored its data in a different location.\n" +"\n" +"If you choose to remove this data, the entire contents of the "" +"evolution" directory will be removed permanently. If you choose to " +"keep this data, then you may manually remove the contents of "" +"evolution" at your convenience.\n" +msgstr "" +"Phiên bản Evolution trước đã cất giữ dữ liệu tại vị trí khác.\n" +"\n" +"Nếu bạn chọn gỡ bỏ dữ liệu này thì sẽ gỡ bỏ hoàn toàn toàn bộ nội dung của " +"thư mục «evolution». Nếu bạn chọn giữ dữ liệu này thì có thể tự gỡ bỏ nội " +"dung «evolution» lúc nào thuận tiện cho bạn.\n" + +#: ../shell/shell.error.xml.h:19 +msgid "Upgrade from previous version failed: {0}" +msgstr "Việc nâng cấp từ phiên bản trước bị lỗi: {0}" + +#: ../shell/shell.error.xml.h:20 +msgid "" +"Upgrading your data and settings will require upto {0} of disk space, but " +"you only have {1} available.\n" +"\n" +"You will need to make more space available in your home directory before you " +"can continue." +msgstr "" +"Nâng cấp các dữ liệu và thiết lập của bạn sẽ cần thiết đến {0} sức chứa trên " +"đĩa, nhưng mà hiện thời bạn chỉ có {1} sẵn sàng.\n" +"\n" +"Như thế thì bạn sẽ phải giải phóng thêm chỗ trống trong thư mục chinh của " +"bạn trước khi có thể tiếp tục." + +#: ../shell/shell.error.xml.h:23 +msgid "" +"Your system configuration does not match your Evolution configuration.\n" +"\n" +"Click help for details" +msgstr "" +"Cấu hình hệ thống bạn không khớp với cấu hình Evolution.\n" +"\n" +"Hãy nhắp vào « Trợ giúp » để xem chi tiết." + +#: ../shell/shell.error.xml.h:26 +msgid "" +"Your system configuration does not match your Evolution configuration:\n" +"\n" +"{0}\n" +"\n" +"Click help for details." +msgstr "" +"Cấu hình hệ thống bạn không khớp với cấu hình Evolution.\n" +"\n" +"{0}\n" +"\n" +"Hãy nhắp vào « Trợ giúp » để xem chi tiết." + +#: ../shell/shell.error.xml.h:31 +msgid "_Forget" +msgstr "_Quên" + +#: ../shell/shell.error.xml.h:32 +msgid "_Keep Data" +msgstr "_Giữ dữ liệu" + +#: ../shell/shell.error.xml.h:33 +msgid "_Remind Me Later" +msgstr "_Nhắc nhở lần sau" + +#: ../shell/shell.error.xml.h:34 +msgid "" +"{1}\n" +"\n" +"If you choose to continue, you may not have access to some of your old " +"data.\n" +msgstr "" +"{1}\n" +"\n" +"Nếu bạn chọn tiếp tục thì có lẽ sẽ không thể truy cập một phần dữ liệu cũ.\n" + +#: ../smime/gui/ca-trust-dialog.c:104 ../smime/gui/ca-trust-dialog.c:96 +#, c-format +msgid "" +"Certificate '%s' is a CA certificate.\n" +"\n" +"Edit trust settings:" +msgstr "" +"Chức nhận « %s » là một chứng nhận CA (nhà cầm quyền chứng nhận).\n" +"\n" +"Sửa đổi thiết lập tin cây:" + +#: ../smime/gui/cert-trust-dialog.c:153 ../smime/gui/cert-trust-dialog.c:145 +msgid "" +"Because you trust the certificate authority that issued this certificate, " +"then you trust the authenticity of this certificate unless otherwise " +"indicated here" +msgstr "" +"Vì bạn tin cây nhà cầm quyền đã phát hành chứng nhận này, thì bạn tin cây " +"xác thực của chứng nhận này trừ khi chỉ thị cách khác ở đây." + +#: ../smime/gui/cert-trust-dialog.c:157 ../smime/gui/cert-trust-dialog.c:149 +msgid "" +"Because you do not trust the certificate authority that issued this " +"certificate, then you do not trust the authenticity of this certificate " +"unless otherwise indicated here" +msgstr "" +"Vì bạn không tin cây nhà cầm quyền đã phát hành chứng nhận này, thì bạn " +"không tin cây xác thực của chứng nhận này trừ khi chỉ thị cách khác ở đây." + +#: ../smime/gui/certificate-manager.c:605 +msgid "Select a certificate to import..." +msgstr "Hãy chọn chứng nhận cần nhập..." + +#: ../smime/gui/certificate-manager.c:692 +msgid "Certificate Name" +msgstr "Tên chứng nhận" + +#: ../smime/gui/certificate-manager.c:492 +msgid "Purposes" +msgstr "Mục đích" + +#: ../smime/lib/e-cert.c:569 ../smime/gui/certificate-manager.c:283 +msgid "Serial Number" +msgstr "Số sản xuất" + +#: ../smime/gui/certificate-manager.c:293 +msgid "Expires" +msgstr "Hết hạn" + +#: ../smime/gui/certificate-viewer.c:342 ../smime/gui/certificate-viewer.c:334 +#, c-format +msgid "Certificate Viewer: %s" +msgstr "Bộ xem chứng nhận: %s" + +#: ../smime/gui/component.c:45 +#, c-format +msgid "Enter the password for `%s'" +msgstr "Nhập mật khẩu cho « %s »" + +#. we're setting the password initially +#: ../smime/gui/component.c:68 +msgid "Enter new password for certificate database" +msgstr "Hãy nhập mật khẩu mới cho cơ sở dữ liệu chứng nhận" + +#: ../smime/gui/component.c:70 +msgid "Enter new password" +msgstr "Hãy nhập mật khẩu mới" + +#. FIXME: add serial no, validity date, uses +#: ../smime/gui/e-cert-selector.c:121 ../smime/gui/e-cert-selector.c:119 +#, c-format +msgid "" +"Issued to:\n" +" Subject: %s\n" +msgstr "" +"Phát hành cho:\n" +" Chủ đề: %s\n" + +#: ../smime/gui/e-cert-selector.c:122 ../smime/gui/e-cert-selector.c:120 +#, c-format +msgid "" +"Issued by:\n" +" Subject: %s\n" +msgstr "" +"Phát hành bởi:\n" +" Chủ đề: %s\n" + +#: ../smime/gui/e-cert-selector.c:174 ../smime/gui/e-cert-selector.c:167 +msgid "Select certificate" +msgstr "Chọn chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:1 +msgid "" +msgstr "" + +#: ../smime/gui/smime-ui.glade.h:2 +msgid "Certificate Fields" +msgstr "Trường chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:3 +msgid "Certificate Hierarchy" +msgstr "Cây chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:4 +msgid "Field Value" +msgstr "Giá trị trường" + +#: ../smime/gui/smime-ui.glade.h:5 +msgid "Fingerprints" +msgstr "Dấu điềm chỉ" + +#: ../smime/gui/smime-ui.glade.h:6 +msgid "Issued By" +msgstr "Phát hành bởi" + +#: ../smime/gui/smime-ui.glade.h:7 +msgid "Issued To" +msgstr "Phát hành cho" + +#: ../smime/gui/smime-ui.glade.h:8 +msgid "This certificate has been verified for the following uses:" +msgstr "Đã xác minh chứng nhận này cho những cách sử dụng theo đây:" + +#: ../smime/gui/smime-ui.glade.h:9 +msgid "Validity" +msgstr "Hợp lệ" + +#: ../smime/gui/smime-ui.glade.h:10 +msgid "Authorities" +msgstr "Nhà cầm quyền" + +#. #-#-#-#-# Compendium04.po (NAME) #-#-#-#-# +#. "A duplicate copy of a program, a disk, or data, made either for archiving purposes or for safeguarding valuable files from loss should the active copy be damaged or destroyed." +#: ../smime/gui/smime-ui.glade.h:11 +msgid "Backup" +msgstr "Sao lưu" + +#: ../smime/gui/smime-ui.glade.h:12 +msgid "Backup All" +msgstr "Lưu trữ tất cả" + +#: ../smime/gui/smime-ui.glade.h:13 +msgid "" +"Before trusting this CA for any purpose, you should examine its certificate " +"and its policy and procedures (if available)." +msgstr "" +"Trước khi tin cây nhà cầm quyền này để làm gì thì bạn nên kiểm tra chứng " +"nhận của nó, và chính thức và thủ tục của nó (nếu công bố)." + +#: ../smime/gui/smime-ui.glade.h:14 ../smime/lib/e-cert.c:1076 +msgid "Certificate" +msgstr "Chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:15 +msgid "Certificate Authority Trust" +msgstr "Độ tin nhà cầm quyền chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:16 +msgid "Certificate details" +msgstr "Chi tiết chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:17 +msgid "Certificates Table" +msgstr "Bảng chứng nhận" + +#: ../smime/gui/smime-ui.glade.h:18 +msgid "Common Name (CN)" +msgstr "Tên chung (TC)" + +#: ../smime/gui/smime-ui.glade.h:19 +msgid "Contact Certificates" +msgstr "Chứng nhận liên lạc" + +#: ../smime/gui/smime-ui.glade.h:21 +msgid "Do not trust the authenticity of this certificate" +msgstr "Đừng tin cây tính xác thực của chứng nhận này." + +#: ../smime/gui/smime-ui.glade.h:22 +msgid "Dummy window only" +msgstr "Chỉ cửa sổ giả" + +#: ../glom/utility_widgets/adddel/adddel.cc:205 +msgid "Edit" +msgstr "Hiệu chỉnh" + +#: ../smime/gui/smime-ui.glade.h:24 +msgid "Email Certificate Trust Settings" +msgstr "Thiết lập Tin cây Chứng nhận Thư điện tử" + +#: ../smime/gui/smime-ui.glade.h:25 +msgid "Email Recipient Certificate" +msgstr "Chứng nhận Người nhận Thư điện tử" + +#: ../smime/gui/smime-ui.glade.h:26 +msgid "Email Signer Certificate" +msgstr "Chứng nhận Ký tên Thư điện tử" + +#: ../smime/gui/smime-ui.glade.h:27 +msgid "Expires On" +msgstr "Hết hạn vào ngày" + +#: ../objects/FS/function.c:684 ../objects/FS/function.c:682 import_gui.c:265 +#: import_gui.c:304 import_gui.c:417 import_gui.c:497 jpilot.c:510 +msgid "Import" +msgstr "Nhập" + +#: ../smime/gui/smime-ui.glade.h:30 +msgid "Issued On" +msgstr "Phát hành vào ngày" + +#: ../smime/gui/smime-ui.glade.h:31 +msgid "MD5 Fingerprint" +msgstr "Dấu điềm chỉ MD5" + +#: ../smime/gui/smime-ui.glade.h:32 +msgid "Organization (O)" +msgstr "Tổ chức (T)" + +#: ../smime/gui/smime-ui.glade.h:33 +msgid "Organizational Unit (OU)" +msgstr "Đơn vị Tổ chức (ĐT)" + +#: ../smime/gui/smime-ui.glade.h:34 +msgid "SHA1 Fingerprint" +msgstr "Dấu điềm chỉ SHA1" + +#: ../smime/gui/smime-ui.glade.h:35 ../smime/lib/e-cert.c:818 +msgid "SSL Client Certificate" +msgstr "Chứng nhận khách SSL" + +#: ../smime/gui/smime-ui.glade.h:36 ../smime/lib/e-cert.c:822 +msgid "SSL Server Certificate" +msgstr "Chứng nhận máy phục vụ SSL" + +#: ../smime/gui/smime-ui.glade.h:38 +msgid "Trust the authenticity of this certificate" +msgstr "Tin cây tính xác thực của chứng nhận này" + +#: ../smime/gui/smime-ui.glade.h:39 +msgid "Trust this CA to identify email users." +msgstr "" +"Tin cây nhà cầm quyền chứng nhận này để nhận diện người dùng thư điện tử." + +#: ../smime/gui/smime-ui.glade.h:40 +msgid "Trust this CA to identify software developers." +msgstr "" +"Tin cây nhà cầm quyền chứng nhận này để nhận diện người phát triển phần mềm." + +#: ../smime/gui/smime-ui.glade.h:41 +msgid "Trust this CA to identify web sites." +msgstr "Tin cây nhà cầm quyền chứng nhận này để nhận diện nơi Mạng." + +#: ../src/f-spot.glade.h:148 ../app/actions/actions.c:199 ../list-ui.c:538 +#: ../glom/mode_design/users/dialog_groups_list.cc:70 +#: ../libgda/gda-server-provider-extra.c:164 +msgid "View" +msgstr "Xem" + +#: ../smime/gui/smime-ui.glade.h:43 +msgid "You have certificates from these organizations that identify you:" +msgstr "Bạn có chứng nhận từ những tổ chức này có nhận diện bạn:" + +#: ../smime/gui/smime-ui.glade.h:44 +msgid "" +"You have certificates on file that identify these certificate authorities:" +msgstr "Bạn đã lưu chứng nhận có nhận diện những nhà cầm quyền chứng nhận này:" + +#: ../smime/gui/smime-ui.glade.h:45 +msgid "You have certificates on file that identify these people:" +msgstr "Bạn đã lưu chứng nhận có nhận diện những người này:" + +#: ../smime/gui/smime-ui.glade.h:46 +msgid "Your Certificates" +msgstr "Chứng nhận của bạn" + +#: ../smime/gui/smime-ui.glade.h:47 +msgid "_Edit CA Trust" +msgstr "_Sửa đổi tính tin cây CA" + +#. XXX we shouldn't be popping up dialogs in this code. +#: ../smime/lib/e-cert-db.c:654 ../smime/lib/e-cert-db.c:651 +msgid "Certificate already exists" +msgstr "Chứng nhận này đã có" + +#: ../smime/lib/e-cert.c:238 ../smime/lib/e-cert.c:248 +msgid "%d/%m/%Y" +msgstr "%d/%m/%Y" + +#: src/fe-gtk/plugingui.c:74 src/query.c:164 +msgid "Version" +msgstr "Phiên bản" + +#: ../smime/lib/e-cert.c:545 +msgid "Version 1" +msgstr "Phiên bản 1" + +#: ../smime/lib/e-cert.c:548 +msgid "Version 2" +msgstr "Phiên bản 2" + +#: ../smime/lib/e-cert.c:551 +msgid "Version 3" +msgstr "Phiên bản 3" + +#: ../smime/lib/e-cert.c:633 +msgid "PKCS #1 MD2 With RSA Encryption" +msgstr "PCKS #1 MD2 với mật mã RSA" + +#: ../smime/lib/e-cert.c:636 +msgid "PKCS #1 MD5 With RSA Encryption" +msgstr "PCKS #1 MD5 với mật mã RSA" + +#: ../smime/lib/e-cert.c:639 +msgid "PKCS #1 SHA-1 With RSA Encryption" +msgstr "PCKS #1 SHA-1 với mật mã RSA" + +#: ../src/red_appwindow.py:92 +msgid "C" +msgstr "C" + +#: ../smime/lib/e-cert.c:645 +msgid "CN" +msgstr "TC" + +#: ../smime/lib/e-cert.c:648 +msgid "OU" +msgstr "ĐT" + +#: ../smime/lib/e-cert.c:651 +msgid "O" +msgstr "T" + +#: ../smime/lib/e-cert.c:654 ../gnopi/cmdmapui.c:154 +msgid "L" +msgstr "L" + +#: ../smime/lib/e-cert.c:657 +msgid "DN" +msgstr "TP" + +#: ../smime/lib/e-cert.c:660 +msgid "DC" +msgstr "DC" + +#: ../smime/lib/e-cert.c:663 +msgid "ST" +msgstr "ST" + +#: ../smime/lib/e-cert.c:666 +msgid "PKCS #1 RSA Encryption" +msgstr "Mật mã RSA PKCS #1" + +#: ../smime/lib/e-cert.c:669 +msgid "Certificate Key Usage" +msgstr "Cách dùng khoá chứng nhận" + +#: ../smime/lib/e-cert.c:672 +msgid "Netscape Certificate Type" +msgstr "Loại chứng nhận Netscape" + +#: ../smime/lib/e-cert.c:675 +msgid "Certificate Authority Key Identifier" +msgstr "Dấu hiệu nhận diện khoá nhà cầm quyền chứng nhận" + +#: ../providers/evolution/gda-calendar-model.c:60 +msgid "UID" +msgstr "UID" + +#: ../smime/lib/e-cert.c:687 +#, c-format +msgid "Object Identifier (%s)" +msgstr "Dấu hiệu nhận diện đối tượng (%s)" + +#: ../smime/lib/e-cert.c:738 +msgid "Algorithm Identifier" +msgstr "Dấu hiệu nhận diện thuật toán" + +#: ../smime/lib/e-cert.c:746 +msgid "Algorithm Parameters" +msgstr "Tham số thuật toán" + +#: ../smime/lib/e-cert.c:768 +msgid "Subject Public Key Info" +msgstr "Thông tin khoá công nhà nhận" + +#: ../smime/lib/e-cert.c:773 +msgid "Subject Public Key Algorithm" +msgstr "Thuật toán khoá công nhà nhận" + +#: ../smime/lib/e-cert.c:788 +msgid "Subject's Public Key" +msgstr "Khoá công nhà nhận" + +#: ../smime/lib/e-cert.c:809 ../smime/lib/e-cert.c:858 +msgid "Error: Unable to process extension" +msgstr "Lỗi: không thể xử lý phần mở rộng" + +#: ../smime/lib/e-cert.c:830 ../smime/lib/e-cert.c:842 +msgid "Object Signer" +msgstr "Bộ ký nhận đối tượng" + +#: ../smime/lib/e-cert.c:834 +msgid "SSL Certificate Authority" +msgstr "Nhà cầm quyền chứng nhận SSL" + +#: ../smime/lib/e-cert.c:838 +msgid "Email Certificate Authority" +msgstr "Nhà cầm quyền chứng nhận thư điện tử" + +#: ../smime/lib/e-cert.c:866 +msgid "Signing" +msgstr "Ký nhận" + +#: ../smime/lib/e-cert.c:870 +msgid "Non-repudiation" +msgstr "Không từ chối" + +#: ../smime/lib/e-cert.c:874 +msgid "Key Encipherment" +msgstr "Mật mã hóa khoá" + +#: ../smime/lib/e-cert.c:878 +msgid "Data Encipherment" +msgstr "Mật mã hóa dữ liệu" + +#: ../smime/lib/e-cert.c:882 +msgid "Key Agreement" +msgstr "Chấp thuận khoá" + +#: ../smime/lib/e-cert.c:886 +msgid "Certificate Signer" +msgstr "Người ký chứng nhận" + +#: ../smime/lib/e-cert.c:890 +msgid "CRL Signer" +msgstr "Người ký CRL" + +#: ../smime/lib/e-cert.c:938 +msgid "Critical" +msgstr "Nghiêm trọng" + +#: ../smime/lib/e-cert.c:940 ../smime/lib/e-cert.c:943 +msgid "Not Critical" +msgstr "Không nghiêm trọng" + +#: ../smime/lib/e-cert.c:964 ../app/widgets/gimpfileprocview.c:253 +#: ../extensions/extensions-manager-ui/extensions-manager-ui.glade.h:2 +#: ../ui/mlview-plugins-window.glade.h:2 +msgid "Extensions" +msgstr "Phần mở rộng" + +# Variable: do not translate/ biến: đừng dịch +#: ../smime/lib/e-cert.c:1035 +#, c-format +msgid "%s = %s" +msgstr "%s = %s" + +#: ../smime/lib/e-cert.c:1091 ../smime/lib/e-cert.c:1211 +msgid "Certificate Signature Algorithm" +msgstr "Thuật toán chữ ký chứng nhận" + +#: ../smime/lib/e-cert.c:1100 +msgid "Issuer" +msgstr "Nhà phát hành" + +#: ../smime/lib/e-cert.c:1154 +msgid "Issuer Unique ID" +msgstr "Thông tin độc nhất nhận biết nhà phát hành" + +#: ../smime/lib/e-cert.c:1173 +msgid "Subject Unique ID" +msgstr "Thông tin độc nhất nhận biết nhà nhận" + +#: ../smime/lib/e-cert.c:1216 +msgid "Certificate Signature Value" +msgstr "Giá trị chữ ký chứng nhận" + +#: ../smime/lib/e-pkcs12.c:266 ../smime/lib/e-pkcs12.c:264 +msgid "PKCS12 File Password" +msgstr "Mật khẩu tập tin PKCS12" + +#: ../smime/lib/e-pkcs12.c:266 ../smime/lib/e-pkcs12.c:264 +msgid "Enter password for PKCS12 file:" +msgstr "Nhập mật khẩu cho tập tin PCKS12:" + +#: ../smime/lib/e-pkcs12.c:365 ../smime/lib/e-pkcs12.c:363 +msgid "Imported Certificate" +msgstr "Chứng nhận đã nhập" + +#: ../tools/evolution-launch-composer.c:324 +msgid "An attachment to add." +msgstr "Đính kèm cần thêm." + +#: ../tools/evolution-launch-composer.c:325 +msgid "Content type of the attachment." +msgstr "Kiểu nội dung của đính kèm." + +#: ../tools/evolution-launch-composer.c:326 +msgid "The filename to display in the mail." +msgstr "Tên tập tin cần hiển thị trong thư." + +#: ../tools/evolution-launch-composer.c:327 +msgid "Description of the attachment." +msgstr "Mô tả đính kèm." + +#: ../tools/evolution-launch-composer.c:328 +msgid "Mark attachment to be shown inline by default." +msgstr "Mặc định là Đánh dấu đính kèm sẽ được hiển thị trực tiếp." + +#: ../tools/evolution-launch-composer.c:329 +msgid "Default subject for the message." +msgstr "Chủ đề mặc định cho thư đó." + +#: ../tools/killev.c:61 +#, c-format +msgid "Could not execute '%s': %s\n" +msgstr "Không thể thực hiện « %s »: %s\n" + +#: ../tools/killev.c:76 +#, c-format +msgid "Shutting down %s (%s)\n" +msgstr "Đang tắt %s (%s)\n" + +#: ../ui/evolution-addressbook.xml.h:2 +msgid "Contact _Preview" +msgstr "_Xem thử liên lạc" + +#: ../ui/evolution-addressbook.xml.h:4 ../ui/evolution-addressbook.xml.h:3 +msgid "Copy Selected Contacts to Another Folder..." +msgstr "Chép các liên lạc được chọn sang thư mục khác..." + +#: ../ui/evolution-addressbook.xml.h:5 ../ui/evolution-calendar.xml.h:2 +#: ../libgnomeui/gnome-app-helper.c:161 +msgid "Copy the selection" +msgstr "Chép đoạn đã chọn" + +#: ../ui/evolution-addressbook.xml.h:6 +msgid "Copy to Folder..." +msgstr "Chép vào thư mục..." + +#: ../glade/gbwidget.c:1859 po/silky.glade.h:87 app/sample-editor.c:449 +msgid "Cut" +msgstr "Cắt" + +#: ../ui/evolution-addressbook.xml.h:8 ../ui/evolution-calendar.xml.h:3 +msgid "Cut the selection" +msgstr "Cắt vùng chọn" + +#: ../ui/evolution-addressbook.xml.h:10 ../ui/evolution-addressbook.xml.h:9 +msgid "Delete selected contacts" +msgstr "Xoá bỏ các liên lạc được chọn" + +#: ../ui/evolution-addressbook.xml.h:10 +msgid "Forward Contact" +msgstr "Chuyển tiếp liên lạc" + +#: ../ui/evolution-addressbook.xml.h:12 ../ui/evolution-addressbook.xml.h:11 +msgid "Move Selected Contacts to Another Folder..." +msgstr "Chuyển các liên lạc được chọn sang thư mục khác..." + +#: ../ui/evolution-addressbook.xml.h:13 ../ui/evolution-addressbook.xml.h:12 +msgid "Move to Folder..." +msgstr "Chuyển sang thư mục..." + +#: ../plug-ins/imagemap/imap_cmd_paste.c:51 ../glade/gbwidget.c:1875 +#: ../glade/property.c:904 po/silky.glade.h:139 app/sample-editor.c:467 +msgid "Paste" +msgstr "Dán" + +#: ../ui/evolution-addressbook.xml.h:15 ../ui/evolution-calendar.xml.h:16 +#: ../libgnomeui/gnome-app-helper.c:166 +msgid "Paste the clipboard" +msgstr "Dán bảng tạm" + +#: ../ui/evolution-addressbook.xml.h:16 ../ui/evolution-addressbook.xml.h:15 +msgid "Previews the contacts to be printed" +msgstr "Xem trước liên lạc cần in" + +#: ../ui/evolution-addressbook.xml.h:19 ../ui/evolution-addressbook.xml.h:18 +msgid "Print selected contacts" +msgstr "In các liên lạc được chọn" + +#: ../ui/evolution-addressbook.xml.h:21 ../ui/evolution-addressbook.xml.h:20 +msgid "Save selected contacts as a VCard." +msgstr "Lưu các liên lạc được chọn là vCard" + +#: ../plug-ins/imagemap/imap_cmd_select_all.c:51 +#: ../plug-ins/rcm/rcm_stock.c:41 +msgid "Select All" +msgstr "Chọn hết" + +#: ../ui/evolution-addressbook.xml.h:23 ../ui/evolution-addressbook.xml.h:22 +msgid "Select all contacts" +msgstr "Chọn mọi liên lạc" + +#: ../ui/evolution-addressbook.xml.h:24 ../ui/evolution-addressbook.xml.h:23 +msgid "Send a message to the selected contacts." +msgstr "Gởi thư cho các liên lạc được chọn." + +#: ../ui/evolution-addressbook.xml.h:25 ../ui/evolution-addressbook.xml.h:24 +msgid "Send message to contact" +msgstr "Gởi thư cho liên lạc" + +#: ../ui/evolution-addressbook.xml.h:26 ../ui/evolution-addressbook.xml.h:25 +msgid "Send selected contacts to another person." +msgstr "Gởi các liên lạc được chọn cho người khác" + +#: ../ui/evolution-addressbook.xml.h:27 ../ui/evolution-addressbook.xml.h:26 +msgid "Show contact preview window" +msgstr "Hiện khung xem trước liên lạc" + +#: ../sheets/SDL.sheet.in.h:19 app/gui.c:1959 +msgid "Stop" +msgstr "Dừng" + +#: ../ui/evolution-addressbook.xml.h:29 ../ui/evolution-addressbook.xml.h:28 +msgid "Stop Loading" +msgstr "Ngưng tải" + +#: ../ui/evolution-addressbook.xml.h:30 ../ui/evolution-addressbook.xml.h:29 +msgid "View the current contact" +msgstr "Xem liên lạc hiện thời" + +#: ../extensions/actions/ephy-actions-extension.c:112 +msgid "_Actions" +msgstr "_Hành động" + +#: ../ui/evolution-addressbook.xml.h:36 ../ui/evolution-addressbook.xml.h:35 +msgid "_Forward Contact..." +msgstr "_Chuyển tiếp liên lạc..." + +#: ../ui/evolution-addressbook.xml.h:43 +msgid "_Send Message to Contact..." +msgstr "_Gởi thư tới liên lạc..." + +#: ../ui/evolution-calendar.xml.h:4 ../gtk/gtkcalendar.c:433 +#: ../gncal/calendar-month-item.c:285 ../gncal/calendar-year-item.c:223 +#: ../libegg/egg-datetime.c:305 ../src/libegg/egg-datetime.c:305 +#: ../Pyblio/GnomeUI/Editor.py:312 src/settings.c:1305 datebook_gui.c:4627 +msgid "Day" +msgstr "Ngày" + +#: ../ui/evolution-calendar.xml.h:6 +msgid "Delete All Occurrences" +msgstr "Xoá bỏ mọi lần" + +#: ../ui/evolution-calendar.xml.h:7 +msgid "Delete the appointment" +msgstr "Xoá bỏ cuộc hẹn" + +#: ../ui/evolution-calendar.xml.h:8 +msgid "Delete this Occurrence" +msgstr "Xoá bỏ lần này" + +#: ../ui/evolution-calendar.xml.h:9 +msgid "Delete this occurrence" +msgstr "Xoá bỏ lần này" + +#: ../ui/evolution-calendar.xml.h:10 +msgid "Go To" +msgstr "Đi tới" + +#: ../ui/evolution-calendar.xml.h:11 ../src/ephy-toolbar.c:267 +#: src/galeon-navigation-button.c:159 +msgid "Go back" +msgstr "Lùi lại" + +#: ../ui/evolution-calendar.xml.h:12 +msgid "Go forward" +msgstr "Đi tiếp" + +#: ../glom/mode_data/notebook_data.cc:28 ../glom/mode_find/notebook_find.cc:27 +#: ../widgets/gtk+.xml.in.h:116 ../src/form-editor/palette.cc:92 +#: ../src/form-editor/widget-util.cc:209 ../src/orca/rolenames.py:298 +msgid "List" +msgstr "Danh sách" + +#: ../libegg/egg-datetime.c:299 ../src/libegg/egg-datetime.c:299 +#: ../Pyblio/GnomeUI/Editor.py:321 datebook_gui.c:4195 datebook_gui.c:4629 +msgid "Month" +msgstr "Tháng" + +#: ../ui/evolution-calendar.xml.h:17 +msgid "Previews the calendar to be printed" +msgstr "Xem trước lịch cần in" + +#: ../ui/evolution-calendar.xml.h:21 +msgid "Print this calendar" +msgstr "In lịch này" + +#: ../ui/evolution-calendar.xml.h:22 ../ui/evolution-tasks.xml.h:17 +#: ../ui/evolution-calendar.xml.h:23 +msgid "Purg_e" +msgstr "_Tẩy" + +#: ../ui/evolution-calendar.xml.h:23 ../ui/evolution-calendar.xml.h:24 +msgid "Purge old appointments and meetings" +msgstr "Tẩy các cuộc hẹn và cuộc họp cũ" + +#: ../ui/evolution-calendar.xml.h:24 ../ui/evolution-calendar.xml.h:25 +msgid "Select _Date" +msgstr "Chọn _ngày" + +#: ../ui/evolution-calendar.xml.h:25 ../calendar/gui/e-calendar-view.c:1519 +#: ../ui/evolution-calendar.xml.h:26 +msgid "Select _Today" +msgstr "Chọn _hôm nay" + +#: ../ui/evolution-calendar.xml.h:26 ../ui/evolution-calendar.xml.h:27 +msgid "Select a specific date" +msgstr "Chọn ngày xác định" + +#: ../ui/evolution-calendar.xml.h:27 ../ui/evolution-calendar.xml.h:28 +msgid "Select today" +msgstr "Chọn hôm nay" + +#: ../ui/evolution-calendar.xml.h:28 ../ui/evolution-calendar.xml.h:29 +msgid "Show as list" +msgstr "Xem kiểu danh sách" + +#: ../ui/evolution-calendar.xml.h:29 ../ui/evolution-calendar.xml.h:30 +msgid "Show one day" +msgstr "Xem một ngày" + +#: ../ui/evolution-calendar.xml.h:30 ../ui/evolution-calendar.xml.h:31 +msgid "Show one month" +msgstr "Xem một tháng" + +#: ../ui/evolution-calendar.xml.h:31 ../ui/evolution-calendar.xml.h:32 +msgid "Show one week" +msgstr "Xem một tuần" + +#: ../ui/evolution-calendar.xml.h:32 ../ui/evolution-calendar.xml.h:33 +msgid "Show the working week" +msgstr "Xem tuần làm việc" + +#: ../ui/evolution-calendar.xml.h:34 ../ui/evolution-calendar.xml.h:35 +msgid "View the current appointment" +msgstr "Xem cuộc hẹn hiện thời" + +#: ../ui/evolution-calendar.xml.h:35 main.c:292 +#: ../ui/evolution-calendar.xml.h:36 ui/galeon.glade.h:54 datebook_gui.c:4184 +#: datebook_gui.c:4628 +msgid "Week" +msgstr "Tuần" + +#: ../ui/evolution-calendar.xml.h:36 ../ui/evolution-calendar.xml.h:37 +msgid "Work Week" +msgstr "Tuần làm việc" + +#: ../ui/evolution-calendar.xml.h:41 ../ui/evolution-calendar.xml.h:42 +msgid "_Open Appointment" +msgstr "Mở _Cuộc hẹn" + +#: ../ui/evolution-composer-entries.xml.h:1 ../ui/evolution-editor.xml.h:7 +msgid "Copy selected text to the clipboard" +msgstr "Sao chép đoạn đã chọn sang bảng tạm" + +#: ../ui/evolution-composer-entries.xml.h:3 ../ui/evolution-editor.xml.h:9 +msgid "Cut selected text to the clipboard" +msgstr "Cắt đoạn đã chọn vào bảng tạm" + +#: ../ui/evolution-composer-entries.xml.h:4 ../ui/evolution-editor.xml.h:10 +msgid "Paste text from the clipboard" +msgstr "Dán đoạn từ bảng tạm" + +#: ../plug-ins/ifscompose/ifscompose.c:1060 ../src/journal.c:3388 +msgid "Select _All" +msgstr "Chọn _hết" + +#: ../ui/evolution-composer-entries.xml.h:6 ../ui/evolution-editor.xml.h:13 +msgid "Select all text" +msgstr "Chọn toàn bộ văn bản" + +#: ../ui/evolution-editor.xml.h:2 +msgid "Click here to attach a file" +msgstr "Nhấn đây để đính kèm tập tin" + +#: ../ui/evolution-editor.xml.h:3 +msgid "Click here to close the current window" +msgstr "Nhấn đây để đóng cửa sổ hiện thời" + +#: ../ui/evolution-editor.xml.h:4 +msgid "Click here to save the current window" +msgstr "Nhấn đây để lưu cửa sổ hiện thời" + +#: ../ui/evolution-editor.xml.h:5 +msgid "Click here to view help availabe" +msgstr "Nhấn đây để xem trợ giúp có sẵn" + +#: ../ui/evolution-editor.xml.h:14 ../ui/evolution-message-composer.xml.h:40 +#: ../ui/evolution-message-composer.xml.h:39 +msgid "_Attachment..." +msgstr "Đính _kèm..." + +#. #-#-#-#-# glade3vi..po (glade3 HEAD) #-#-#-#-# +#. File +#: ../src/mlview-app.cc:277 ../Pyblio/GnomeUI/Document.py:144 +#: ../src/glade-gtk.c:2312 po/silky.glade.h:215 app/menubar.c:685 +msgid "_File" +msgstr "_Tập tin" + +#: ../plug-ins/imagemap/imap_polygon.c:521 ../src/main.c:595 +msgid "_Insert" +msgstr "_Chèn" + +#: ../ui/evolution-event-editor.xml.h:2 +msgid "All day Event" +msgstr "Sự kiện nguyên ngày" + +#: ../ui/evolution-event-editor.xml.h:3 ../ui/evolution-task-editor.xml.h:1 +msgid "Classify as Confidential" +msgstr "Phân loại là Tin tưởng" + +#: ../ui/evolution-event-editor.xml.h:4 ../ui/evolution-task-editor.xml.h:2 +msgid "Classify as Private" +msgstr "Phân loại là Riêng" + +#: ../ui/evolution-event-editor.xml.h:5 ../ui/evolution-task-editor.xml.h:3 +msgid "Classify as public" +msgstr "Phân loại là Công" + +#: ../ui/evolution-event-editor.xml.h:6 +msgid "Click here to set or unset alarms for this event" +msgstr "Nhấn đây để lập hay bỏ lập báo động cho sự kiện này" + +#: ../ui/evolution-event-editor.xml.h:8 ../ui/evolution-task-editor.xml.h:5 +msgid "Insert advanced send options" +msgstr "Chèn tùy chọn gởi cấp cao" + +#: ../ui/evolution-event-editor.xml.h:9 +msgid "Make this a recurring event" +msgstr "Đặt là sự kiện lặp" + +#: ../ui/evolution-event-editor.xml.h:10 ../ui/evolution-task-editor.xml.h:6 +msgid "Pu_blic" +msgstr "_Công" + +#: ../ui/evolution-event-editor.xml.h:11 +msgid "Query free / busy information for the attendees" +msgstr "Truy vấn thông tin Rảnh/Bận cho các người dự" + +#: ../ui/evolution-event-editor.xml.h:12 ../ui/evolution-task-editor.xml.h:7 +msgid "R_ole Field" +msgstr "Trường _Vai trò" + +#: ../ui/evolution-event-editor.xml.h:15 ../ui/evolution-task-editor.xml.h:9 +msgid "Show Time _Zone" +msgstr "Hiện múi _giờ" + +#: ../ui/evolution-event-editor.xml.h:16 +msgid "Show time as b_usy" +msgstr "Hiện giờ là _bận" + +#: ../ui/evolution-event-editor.xml.h:17 ../ui/evolution-task-editor.xml.h:12 +msgid "Toggles whether the Attendee Type field is displayed" +msgstr "Bật tắt hiển thị trường Kiểu người dự" + +#: ../ui/evolution-event-editor.xml.h:18 ../ui/evolution-task-editor.xml.h:13 +msgid "Toggles whether the RSVP field is displayed" +msgstr "Bật tắt hiển thị trường RSVP" + +#: ../ui/evolution-event-editor.xml.h:19 ../ui/evolution-task-editor.xml.h:14 +msgid "Toggles whether the Role field is displayed" +msgstr "Bật tắt hiển thị trường Vai trò" + +#: ../ui/evolution-event-editor.xml.h:20 ../ui/evolution-task-editor.xml.h:15 +msgid "Toggles whether the Status field is displayed" +msgstr "Bật tắt hiển thị trường Trạng thái" + +#: ../ui/evolution-event-editor.xml.h:21 ../ui/evolution-task-editor.xml.h:16 +msgid "Toggles whether the time zone is displayed" +msgstr "Bật tắt hiển thị múi giờ" + +#: ../ui/evolution-event-editor.xml.h:22 ../ui/evolution-task-editor.xml.h:17 +msgid "Toggles whether to display categories" +msgstr "Bật tắt hiển thị các phân loại" + +#: ../ui/evolution-event-editor.xml.h:23 +msgid "Toggles whether to have All day Event" +msgstr "Bật tắt có dự kiện nguyên ngày" + +#: ../ui/evolution-event-editor.xml.h:24 +msgid "Toggles whether to show time as busy" +msgstr "Bật tắt hiển thị giờ là bận" + +#: ../ui/evolution-event-editor.xml.h:25 +msgid "_Alarms" +msgstr "_Báo động" + +#: ../ui/evolution-event-editor.xml.h:26 +msgid "_All day Event" +msgstr "Dự kiện _nguyên ngày" + +#: ../ui/evolution-event-editor.xml.h:28 ../ui/evolution-task-editor.xml.h:19 +msgid "_Classification" +msgstr "_Phân loại" + +#: ../ui/evolution-event-editor.xml.h:29 ../ui/evolution-task-editor.xml.h:20 +msgid "_Confidential" +msgstr "_Tin tưởng" + +#: ../ui/evolution-event-editor.xml.h:30 ../ui/evolution-task-editor.xml.h:21 +#: ../gnomecard/card-editor.glade.h:60 +msgid "_Private" +msgstr "_Riêng" + +#: ../ui/evolution-event-editor.xml.h:31 ../ui/evolution-task-editor.xml.h:22 +msgid "_RSVP" +msgstr "_RSVP" + +#: ../ui/evolution-event-editor.xml.h:32 ../ui/evolution-task-editor.xml.h:24 +msgid "_Status Field" +msgstr "Trường _Trạng thái" + +#: ../ui/evolution-event-editor.xml.h:33 ../ui/evolution-task-editor.xml.h:25 +msgid "_Type Field" +msgstr "Trường _Kiểu" + +#: ../ui/evolution-executive-summary.xml.h:1 +msgid "Customize My Evolution" +msgstr "Tùy biến Evolution của tôi" + +#: ../ui/evolution-mail-global.xml.h:2 +msgid "Cancel the current mail operation" +msgstr "Hủy tác vụ thư tín hiện thời" + +#: ../ui/evolution-mail-global.xml.h:3 +msgid "Copy the selected folder into another folder" +msgstr "Sao chép thư mục được chọn sang thư mục khác" + +#: ../ui/evolution-mail-global.xml.h:4 +msgid "Create a new folder for storing mail" +msgstr "Tạo thư mục mới để lưu thư" + +#: ../ui/evolution-mail-global.xml.h:5 +msgid "Create or edit Search Folder definitions" +msgstr "Tạo hoặc sửa lời định nghĩa thư mục tìm kiếm" + +#: ../ui/evolution-mail-global.xml.h:6 +msgid "Create or edit rules for filtering new mail" +msgstr "Tạo hoặc sửa đổi quy tắc lọc thư mới" + +#: ../ui/evolution-mail-global.xml.h:8 ../ui/evolution-mail-list.xml.h:7 +#: ../ui/evolution-subscribe.xml.h:2 ../ui/evolution-mail-list.xml.h:6 +msgid "F_older" +msgstr "Danh _mục" + +#: ../ui/evolution-mail-global.xml.h:9 +msgid "Message F_ilters" +msgstr "Bộ _lọc thư" + +#: ../ui/evolution-mail-global.xml.h:10 ../ui/evolution-mail-global.xml.h:11 +msgid "Message _Preview" +msgstr "Xem thư _trước" + +#: ../ui/evolution-mail-global.xml.h:11 ../ui/evolution-mail-global.xml.h:12 +msgid "Move the selected folder into another folder" +msgstr "Chuyển thư mục được chọn tới thư mục khác" + +#. Alphabetical by name, yo +#: ../ui/evolution-mail-global.xml.h:13 +msgid "Permanently remove all deleted messages from all folders" +msgstr "Gỡ bỏ hoàn toàn mọi thư đã xoá bỏ ra mọi thư mục" + +#: ../ui/evolution-mail-global.xml.h:14 ../ui/evolution-mail-global.xml.h:15 +msgid "Search F_olders" +msgstr "Tìm kiếm trong _thư mục" + +#: ../ui/evolution-mail-global.xml.h:15 ../ui/evolution-mail-global.xml.h:16 +msgid "Show message preview window" +msgstr "Hiện khung xem thư trước" + +#: ../ui/evolution-mail-global.xml.h:16 +msgid "Subscribe or unsubscribe to folders on remote servers" +msgstr "Đăng ký hoặc hủy đăng ký thư mục trên máy chủ từ xa" + +#: ../ui/evolution-mail-global.xml.h:17 ../ui/evolution-mail-global.xml.h:18 +msgid "_Copy Folder To..." +msgstr "_Chép thư mục vào..." + +#: ../ui/evolution-mail-global.xml.h:18 ../ui/evolution-mail-global.xml.h:19 +msgid "_Move Folder To..." +msgstr "_Chuyển thư mục sang..." + +#: ../ui/evolution-mail-global.xml.h:23 ../ui/evolution-mail-global.xml.h:22 +msgid "_Subscriptions" +msgstr "_Mục đăng ký" + +#: ../ui/evolution-mail-list.xml.h:1 +msgid "Change the name of this folder" +msgstr "Thay đổi tên thư mục này" + +#: ../ui/evolution-mail-list.xml.h:2 +msgid "Change the properties of this folder" +msgstr "Thay đổi thuộc tính thư mục này" + +#: ../ui/evolution-mail-list.xml.h:3 ../ui/evolution-mail-message.xml.h:12 +msgid "Copy selected message(s) to the clipboard" +msgstr "Sao chép các thư đã chọn sang bảng tạm" + +#: ../ui/evolution-mail-list.xml.h:4 +msgid "Cut selected message(s) to the clipboard" +msgstr "Cắt các thư đã chọn vào bảng tạm" + +#: ../ui/evolution-mail-list.xml.h:6 ../ui/evolution-mail-list.xml.h:5 +msgid "E_xpunge" +msgstr "_Xoá hẳn" + +#: ../ui/evolution-mail-list.xml.h:8 ../ui/evolution-mail-list.xml.h:7 +msgid "Group By _Threads" +msgstr "Nhóm lại theo _mạch" + +#: ../ui/evolution-mail-list.xml.h:9 ../ui/evolution-mail-list.xml.h:8 +msgid "Hide S_elected Messages" +msgstr "Ẩn các thư đã _chọn" + +#: ../ui/evolution-mail-list.xml.h:10 ../ui/evolution-mail-list.xml.h:9 +msgid "Hide _Deleted Messages" +msgstr "Ẩn các thư đã _xoá bỏ" + +#: ../ui/evolution-mail-list.xml.h:11 ../ui/evolution-mail-list.xml.h:10 +msgid "Hide _Read Messages" +msgstr "Ẩn các thư đã _đọc" + +#: ../ui/evolution-mail-list.xml.h:12 ../ui/evolution-mail-list.xml.h:11 +msgid "" +"Hide deleted messages rather than displaying them with a line through them" +msgstr "Ẩn các thư đã xoá bỏ thay vì hiển thị chúng dạng gạch đè" + +#: ../ui/evolution-mail-list.xml.h:13 +msgid "Mar_k Messages as Read" +msgstr "Đánh dấu thư Đã đọ_c" + +#: ../ui/evolution-mail-list.xml.h:14 ../ui/evolution-mail-message.xml.h:68 +#: ../ui/evolution-mail-list.xml.h:13 +msgid "Paste message(s) from the clipboard" +msgstr "Dán các thư từ bảng tạm" + +#: ../ui/evolution-mail-list.xml.h:15 ../ui/evolution-mail-list.xml.h:14 +msgid "Permanently remove all deleted messages from this folder" +msgstr "Gỡ bỏ hoàn toàn mọi thư đã xoá bỏ trong thư mục này" + +#: ../ui/evolution-mail-list.xml.h:16 ../ui/evolution-mail-list.xml.h:15 +msgid "Permanently remove this folder" +msgstr "Gỡ bỏ hoàn toàn thư mục này" + +#: ../ui/evolution-mail-list.xml.h:17 +msgid "Select Message _Thread" +msgstr "Chọn _nhánh thư" + +#: ../ui/evolution-mail-list.xml.h:18 ../ui/evolution-mail-list.xml.h:16 +msgid "Select _All Messages" +msgstr "Chọn _mọi thư" + +#: ../ui/evolution-mail-list.xml.h:19 ../ui/evolution-mail-list.xml.h:17 +msgid "Select all and only the messages that are not currently selected" +msgstr "Chọn tất cả và chỉ những thư hiện thời không được chọn" + +#: ../ui/evolution-mail-list.xml.h:20 ../ui/evolution-mail-list.xml.h:18 +msgid "Select all messages in the same thread as the selected message" +msgstr "Chọn tất cả nhưng thư trong cùng mạch với thư đã chọn" + +#: ../ui/evolution-mail-list.xml.h:21 ../ui/evolution-mail-list.xml.h:19 +msgid "Select all visible messages" +msgstr "Chọn mọi thư có thể thấy" + +#: ../ui/evolution-mail-list.xml.h:22 ../ui/evolution-mail-list.xml.h:20 +msgid "Sh_ow Hidden Messages" +msgstr "_Hiển thị thư bị ẩn" + +#: ../ui/evolution-mail-list.xml.h:23 ../ui/evolution-mail-list.xml.h:21 +msgid "Show messages that have been temporarily hidden" +msgstr "Hiển thị các thư đang bị giấu tạm thời" + +#: ../ui/evolution-mail-list.xml.h:24 ../ui/evolution-mail-list.xml.h:22 +msgid "Temporarily hide all messages that have already been read" +msgstr "Ẩn tạm thời mọi thư đã đọc" + +#: ../ui/evolution-mail-list.xml.h:25 ../ui/evolution-mail-list.xml.h:23 +msgid "Temporarily hide the selected messages" +msgstr "Ẩn tạm thời những thư được chọn" + +#: ../ui/evolution-mail-list.xml.h:26 ../ui/evolution-mail-list.xml.h:24 +msgid "Threaded Message list" +msgstr "Danh sách thư theo mạch" + +#: ../ui/evolution-mail-message.xml.h:1 +msgid "A_dd Sender to Address Book" +msgstr "Thêm người _gởi vào Sổ địa chỉ" + +#: ../ui/evolution-mail-message.xml.h:2 +msgid "A_pply Filters" +msgstr "Á_p dụng bộ lọc" + +#: ../ui/evolution-mail-message.xml.h:4 ../ui/evolution-mail-message.xml.h:3 +msgid "Add Sender to Address Book" +msgstr "Thêm người gởi vào Sổ địa chỉ" + +#: ../ui/evolution-mail-message.xml.h:5 ../ui/evolution-mail-message.xml.h:4 +msgid "All Message _Headers" +msgstr "Các dòng đầu thư" + +#: ../ui/evolution-mail-message.xml.h:6 ../ui/evolution-mail-message.xml.h:5 +msgid "Apply filter rules to the selected messages" +msgstr "Áp dụng bộ lọc vào các thư đã chọn" + +#: ../ui/evolution-mail-message.xml.h:7 ../ui/evolution-mail-message.xml.h:6 +msgid "Check for _Junk" +msgstr "Kiểm tra tìm thư rác" + +#: ../ui/evolution-mail-message.xml.h:8 ../ui/evolution-mail-message.xml.h:7 +msgid "Compose _New Message" +msgstr "Soạn thư _mới" + +#: ../ui/evolution-mail-message.xml.h:9 ../ui/evolution-mail-message.xml.h:8 +msgid "Compose a reply to all of the recipients of the selected message" +msgstr "Soạn thư trả lời cho mọi người nhận thư được chọn" + +#: ../ui/evolution-mail-message.xml.h:10 ../ui/evolution-mail-message.xml.h:9 +msgid "Compose a reply to the mailing list of the selected message" +msgstr "Soạn thư trả lời cho hộp thư chung của thư được chọn" + +#: ../ui/evolution-mail-message.xml.h:11 ../ui/evolution-mail-message.xml.h:10 +msgid "Compose a reply to the sender of the selected message" +msgstr "Soạn thư trả lời cho người gởi thư được chọn" + +#: ../ui/evolution-mail-message.xml.h:13 +msgid "Copy selected messages to another folder" +msgstr "Sao chép các thư được chọn sang thư mục khác" + +#: ../ui/evolution-mail-message.xml.h:14 +msgid "Create R_ule" +msgstr "Tạo _quy tắc" + +#: ../ui/evolution-mail-message.xml.h:15 +msgid "Create a Search Folder for these recipients" +msgstr "Tạo thư mục tìm kiếm cho những người nhận này" + +#: ../ui/evolution-mail-message.xml.h:16 +msgid "Create a Search Folder for this mailing list" +msgstr "Tạo thư mục tìm kiếm cho hộp thư chung này" + +#: ../ui/evolution-mail-message.xml.h:17 +msgid "Create a Search Folder for this sender" +msgstr "Tạo thư mục tìm kiếm cho người gởi này" + +#: ../ui/evolution-mail-message.xml.h:18 +msgid "Create a Search Folder for this subject" +msgstr "Tạo thư mục tìm kiếm cho chủ đề này" + +#: ../ui/evolution-mail-message.xml.h:19 +msgid "Create a rule to filter messages from this sender" +msgstr "Tạo quy tắc để lọc mọi thư từ người gởi này" + +#: ../ui/evolution-mail-message.xml.h:20 +msgid "Create a rule to filter messages to these recipients" +msgstr "Tạo quy tắc để lọc mọi thư được gởi cho những người nhận này" + +#: ../ui/evolution-mail-message.xml.h:21 +msgid "Create a rule to filter messages to this mailing list" +msgstr "Tạo quy tắc để lọc mọi thư được gởi cho hộp thư chung này" + +#: ../ui/evolution-mail-message.xml.h:22 +msgid "Create a rule to filter messages with this subject" +msgstr "Tạo quy tắc để lọc mọi thư có chủ đề này" + +#: ../ui/evolution-mail-message.xml.h:24 ../src/ephy-window.c:214 +msgid "Decrease the text size" +msgstr "Giảm cỡ chữ" + +#: ../ui/evolution-mail-message.xml.h:26 +msgid "Display the next important message" +msgstr "Hiển thị thư quan trọng kế tiếp" + +#: ../ui/evolution-mail-message.xml.h:27 +msgid "Display the next message" +msgstr "Hiển thị thư kế tiếp" + +#: ../ui/evolution-mail-message.xml.h:28 +msgid "Display the next unread message" +msgstr "Hiển thị thư chưa đọc kế tiếp" + +#: ../ui/evolution-mail-message.xml.h:29 +msgid "Display the next unread thread" +msgstr "Hiển thị mạch chưa đọc kế tiếp" + +#: ../ui/evolution-mail-message.xml.h:30 +msgid "Display the previous important message" +msgstr "Hiển thị thư quan trọng trước đó" + +#: ../ui/evolution-mail-message.xml.h:31 +msgid "Display the previous message" +msgstr "Hiển thị thư trước đó" + +#: ../ui/evolution-mail-message.xml.h:32 +msgid "Display the previous unread message" +msgstr "Hiển thị thư chưa đọc trước đó" + +#: ../ui/evolution-mail-message.xml.h:33 +msgid "F_orward As..." +msgstr "_Chuyển tiếp dạng..." + +#: ../ui/evolution-mail-message.xml.h:34 ../ui/evolution-mail-message.xml.h:33 +msgid "Filter on Mailing _List..." +msgstr "Lọc theo _hộp thư chung..." + +#: ../ui/evolution-mail-message.xml.h:35 ../ui/evolution-mail-message.xml.h:34 +msgid "Filter on Se_nder..." +msgstr "Lọc theo Người _gởi..." + +#: ../ui/evolution-mail-message.xml.h:36 ../ui/evolution-mail-message.xml.h:35 +msgid "Filter on _Recipients..." +msgstr "Lọc theo _Người nhận..." + +#: ../ui/evolution-mail-message.xml.h:37 ../ui/evolution-mail-message.xml.h:36 +msgid "Filter on _Subject..." +msgstr "Lọc theo _Chủ đề..." + +#: ../ui/evolution-mail-message.xml.h:38 ../ui/evolution-mail-message.xml.h:37 +msgid "Filter the selected messages for junk status" +msgstr "Lọc các thư được chọn để quyết định trạng thái rác" + +#: ../ui/evolution-mail-message.xml.h:39 ../ui/evolution-mail-message.xml.h:38 +msgid "Flag selected message(s) for follow-up" +msgstr "Đặt cờ trên các thư được chọn để theo dõi tiếp" + +#: ../ui/evolution-mail-message.xml.h:40 ../ui/evolution-mail-message.xml.h:39 +msgid "Follow _Up..." +msgstr "Th_eo dõi tiếp..." + +#: ../ui/evolution-mail-message.xml.h:41 ../ui/evolution-mail-message.xml.h:40 +msgid "Force images in HTML mail to be loaded" +msgstr "Ép tải ảnh trong thư HTML" + +#: ../ui/evolution-mail-message.xml.h:43 ../ui/evolution-mail-message.xml.h:42 +msgid "Forward the selected message in the body of a new message" +msgstr "Chuyển tiếp thư được chọn trong thân thư mới" + +#: ../ui/evolution-mail-message.xml.h:44 ../ui/evolution-mail-message.xml.h:43 +msgid "Forward the selected message quoted like a reply" +msgstr "Chuyển tiếp thư được chọn được trích dẫn là trả lời" + +#: ../ui/evolution-mail-message.xml.h:45 ../ui/evolution-mail-message.xml.h:44 +msgid "Forward the selected message to someone" +msgstr "Chuyển tiếp thông điệp được chọn tới người khác" + +#: ../ui/evolution-mail-message.xml.h:46 ../ui/evolution-mail-message.xml.h:45 +msgid "Forward the selected message to someone as an attachment" +msgstr "Chuyển tiếp thông điệp được chọn tới người khác như là đính kèm" + +#: ../ui/evolution-mail-message.xml.h:47 ../src/ephy-window.c:211 +#: ../ui/evolution-mail-message.xml.h:46 +msgid "Increase the text size" +msgstr "Tăng cỡ chữ" + +#: ../ui/evolution-mail-message.xml.h:49 ../ui/evolution-mail-message.xml.h:48 +msgid "Mar_k as" +msgstr "_Nhãn là" + +#: ../ui/evolution-mail-message.xml.h:50 ../ui/evolution-mail-message.xml.h:49 +msgid "Mark the selected message(s) as having been read" +msgstr "Đánh dấu các thư được chọn có đã đọc" + +#: ../ui/evolution-mail-message.xml.h:51 ../ui/evolution-mail-message.xml.h:50 +msgid "Mark the selected message(s) as important" +msgstr "Đánh dấu cho các thư được chọn là quan trọng" + +#: ../ui/evolution-mail-message.xml.h:52 ../ui/evolution-mail-message.xml.h:51 +msgid "Mark the selected message(s) as junk" +msgstr "Đánh dấu các thư được chọn là rác" + +#: ../ui/evolution-mail-message.xml.h:53 ../ui/evolution-mail-message.xml.h:52 +msgid "Mark the selected message(s) as not being junk" +msgstr "Đánh dấu các thư được chọn không phải là rác" + +#: ../ui/evolution-mail-message.xml.h:54 ../ui/evolution-mail-message.xml.h:53 +msgid "Mark the selected message(s) as not having been read" +msgstr "Đánh dấu các thư được chọn có chưa đọc" + +#: ../ui/evolution-mail-message.xml.h:55 ../ui/evolution-mail-message.xml.h:54 +msgid "Mark the selected message(s) as unimportant" +msgstr "Đánh dấu các thư được chọn không phải là quan trọng" + +#: ../ui/evolution-mail-message.xml.h:56 ../ui/evolution-mail-message.xml.h:55 +msgid "Mark the selected messages for deletion" +msgstr "Đánh dấu các thư được chọn cần xoá bỏ" + +#: ../ui/evolution-mail-message.xml.h:58 +msgid "Move selected message(s) to another folder" +msgstr "Di chuyển các thư được chọn sang thư mục khác" + +#: ../ui/evolution-mail-message.xml.h:60 +msgid "Next _Important Message" +msgstr "Thư _quan trọng kế" + +#: ../ui/evolution-mail-message.xml.h:61 +msgid "Next _Thread" +msgstr "_Mạch kế" + +#: ../ui/evolution-mail-message.xml.h:62 +msgid "Next _Unread Message" +msgstr "Thư _chưa đọc kế" + +#: ../ui/evolution-mail-message.xml.h:63 +msgid "Not Junk" +msgstr "Không phải rác" + +#: ../ui/evolution-mail-message.xml.h:64 +msgid "Open a window for composing a mail message" +msgstr "Mở cửa sổ soạn thư" + +#: ../ui/evolution-mail-message.xml.h:65 +msgid "Open the selected message in a new window" +msgstr "Mở thông điệp được chọn trong cửa sổ mới" + +#: ../ui/evolution-mail-message.xml.h:66 +msgid "Open the selected message in the composer for editing" +msgstr "Mở thông điệp được chọn trong bộ soạn thảo để hiệu chỉnh" + +#: ../ui/evolution-mail-message.xml.h:67 +msgid "P_revious Unread Message" +msgstr "Thư chưa đọc t_rước" + +#: ../ui/evolution-mail-message.xml.h:69 +msgid "Pos_t New Message to Folder" +msgstr "Gởi thư mới _tới thư mục" + +#: ../ui/evolution-mail-message.xml.h:70 ../ui/evolution-mail-message.xml.h:69 +msgid "Post a Repl_y" +msgstr "Gởi t_rả lời" + +#: ../ui/evolution-mail-message.xml.h:71 ../ui/evolution-mail-global.xml.h:14 +msgid "Post a message to a Public folder" +msgstr "Gởi thư tới thư mục Công cộng" + +#: ../ui/evolution-mail-message.xml.h:72 +msgid "Post a reply to a message in a Public folder" +msgstr "Gởi trả lời thông điệp trong thư mục Công cộng" + +#: ../ui/evolution-mail-message.xml.h:73 ../ui/evolution-mail-message.xml.h:71 +msgid "Pr_evious Important Message" +msgstr "Thư quan trọng t_rước" + +#: ../ui/evolution-mail-message.xml.h:74 ../ui/evolution-mail-message.xml.h:72 +msgid "Preview the message to be printed" +msgstr "Xem trước thông điệp cần in" + +#: ../ui/evolution-mail-message.xml.h:78 ../ui/evolution-mail-message.xml.h:76 +msgid "Print this message" +msgstr "In thư này" + +#: ../ui/evolution-mail-message.xml.h:79 ../ui/evolution-mail-message.xml.h:77 +msgid "Re_direct" +msgstr "Chuyển _hướng" + +#: ../ui/evolution-mail-message.xml.h:80 ../ui/evolution-mail-message.xml.h:78 +msgid "Redirect (bounce) the selected message to someone" +msgstr "Chuyển hướng (bounce: nảy lên) thư được chọn tới người khác" + +#: ../ui/evolution-mail-message.xml.h:85 ../ui/evolution-mail-message.xml.h:82 +msgid "Reset the text to its original size" +msgstr "Phục hồi kích thước chữ gốc" + +#: ../ui/evolution-mail-message.xml.h:86 ../ui/evolution-mail-message.xml.h:83 +msgid "Save the message as a text file" +msgstr "Lưu thư là tập tin văn bản" + +#: ../ui/evolution-mail-message.xml.h:87 ../ui/evolution-mail-message.xml.h:84 +msgid "Search Folder from Mailing _List..." +msgstr "Thư mục tìm kiếm trên _Hộp thư chung..." + +#: ../ui/evolution-mail-message.xml.h:88 ../ui/evolution-mail-message.xml.h:85 +msgid "Search Folder from Recipients..." +msgstr "Thư mục tìm kiếm trên _Người nhận..." + +#: ../ui/evolution-mail-message.xml.h:89 ../ui/evolution-mail-message.xml.h:86 +msgid "Search Folder from S_ubject..." +msgstr "Thư mục tìm kiếm trên _Chủ đề..." + +#: ../ui/evolution-mail-message.xml.h:90 ../ui/evolution-mail-message.xml.h:87 +msgid "Search Folder from Sen_der..." +msgstr "Thư mục tìm kiếm trên Người _gởi..." + +#: ../ui/evolution-mail-message.xml.h:91 ../ui/evolution-mail-message.xml.h:88 +msgid "Search for text in the body of the displayed message" +msgstr "Tìm đoạn trong thân thư đã hiển thị" + +#: ../ui/evolution-mail-message.xml.h:92 ../ui/evolution-mail-message.xml.h:89 +msgid "Select _All Text" +msgstr "Chọn toàn bộ v_ăn bản" + +#: ../ui/evolution-mail-message.xml.h:93 ../ui/evolution-mail-message.xml.h:90 +msgid "Select all the text in a message" +msgstr "Chọn mọi văn bản trong thư" + +#: ../ui/evolution-mail-message.xml.h:94 ../ui/evolution-mail-message.xml.h:91 +msgid "Set up the page settings for your current printer" +msgstr "Thiết lập trang cho máy in hiện thời" + +#: ../ui/evolution-mail-message.xml.h:95 +msgid "Show a blinking cursor in the body of displayed messages" +msgstr "Hiển thị con chạy nháy trong phần thân các thư đã hiển thị" + +#: ../ui/evolution-mail-message.xml.h:96 ../ui/evolution-mail-message.xml.h:93 +msgid "Show message in the normal style" +msgstr "Hiện thông điệp theo cách bình thường" + +#: ../ui/evolution-mail-message.xml.h:97 ../ui/evolution-mail-message.xml.h:94 +msgid "Show message with all email headers" +msgstr "Hiện thư với mọi dòng đầu thư" + +#: ../ui/evolution-mail-message.xml.h:98 ../ui/evolution-mail-message.xml.h:95 +msgid "Show the raw email source of the message" +msgstr "Hiện thư thô, mã nguồn" + +#: ../ui/evolution-mail-message.xml.h:99 ../ui/evolution-mail-message.xml.h:96 +msgid "Un-delete the selected messages" +msgstr "Hủy xoá bỏ những thư được chọn" + +#: ../ui/evolution-mail-message.xml.h:100 +#: ../ui/evolution-mail-message.xml.h:97 +msgid "Uni_mportant" +msgstr "_Không quan trọng" + +#: ../ui/evolution-mail-message.xml.h:102 +msgid "_Attached" +msgstr "Gởi _kèm" + +#: ../ui/evolution-mail-message.xml.h:103 +#: ../ui/evolution-mail-message.xml.h:99 +msgid "_Caret Mode" +msgstr "Chế độn con _nháy" + +#: ../ui/evolution-mail-message.xml.h:106 +#: ../ui/evolution-mail-message.xml.h:102 +msgid "_Delete Message" +msgstr "_Xoá bỏ thư" + +#: ../ui/evolution-mail-message.xml.h:108 +#: ../ui/evolution-mail-message.xml.h:104 +msgid "_Find in Message..." +msgstr "_Tìm trong thư" + +#: ../ui/evolution-mail-message.xml.h:110 +#: ../ui/evolution-mail-message.xml.h:106 +msgid "_Go To" +msgstr "Đ_i tới" + +#: ../ui/evolution-mail-message.xml.h:111 +#: ../ui/evolution-mail-message.xml.h:107 +msgid "_Important" +msgstr "_Quan trọng" + +#: ../ui/evolution-mail-message.xml.h:112 +msgid "_Inline" +msgstr "Trực t_iếp" + +#: ../ui/evolution-mail-message.xml.h:113 +#: ../ui/evolution-mail-message.xml.h:108 +msgid "_Junk" +msgstr "_Rác" + +#: ../ui/evolution-mail-message.xml.h:114 +#: ../ui/evolution-mail-message.xml.h:109 +msgid "_Load Images" +msgstr "Tải ả_nh" + +#: ../ui/evolution-mail-message.xml.h:118 ../gtk/gtkstock.c:413 +msgid "_Normal Size" +msgstr "_Cỡ thường" + +#: ../ui/evolution-mail-message.xml.h:119 +#: ../ui/evolution-mail-message.xml.h:114 +msgid "_Not Junk" +msgstr "Không _phải rác" + +#: ../ui/evolution-mail-message.xml.h:120 +#: ../ui/evolution-mail-message.xml.h:115 +msgid "_Open in New Window" +msgstr "Mở trong cửa sổ _mới" + +#: ../ui/evolution-mail-message.xml.h:123 +msgid "_Quoted" +msgstr "Trích _dẫn" + +#: ../ui/evolution-mail-message.xml.h:126 +#: ../ui/evolution-mail-message.xml.h:120 +msgid "_Save Message..." +msgstr "_Lưu thư..." + +#: ../ui/evolution-mail-message.xml.h:127 +#: ../ui/evolution-mail-message.xml.h:121 +msgid "_Undelete Message" +msgstr "_Hủy xoá bỏ thư" + +#: ../ui/evolution-mail-message.xml.h:129 ../src/planner-gantt-view.c:164 +msgid "_Zoom In" +msgstr "_Phóng to" + +#: ../src/ggv-ui.xml.h:3 ../src/widgets/cria-main-window.c:1576 +msgid "Close this window" +msgstr "Đóng cửa sổ này" + +#: ../ui/evolution-mail-messagedisplay.xml.h:3 ../ui/evolution.xml.h:16 +#: ../ui/evolution.xml.h:17 ../gedit/gedit-ui.xml.h:24 +msgid "Main toolbar" +msgstr "Thanh công cụ chính" + +#: ../ui/evolution-memos.xml.h:3 +msgid "Copy selected memo" +msgstr "Chép ghi nhớ đã chọn" + +#: ../ui/evolution-memos.xml.h:5 +msgid "Cut selected memo" +msgstr "Cắt ghi nhớ đã chọn" + +#: ../ui/evolution-memos.xml.h:7 +msgid "Delete selected memos" +msgstr "Xoá bỏ các ghi nhớ đã chọn" + +#: ../ui/evolution-memos.xml.h:9 +msgid "Paste memo from the clipboard" +msgstr "Dán ghi nhớ từ bảng tạm" + +#: ../ui/evolution-memos.xml.h:10 +msgid "Previews the list of memos to be printed" +msgstr "Xem thử danh sách các ghi nhớ cần in" + +#: ../ui/evolution-memos.xml.h:13 +msgid "Print the list of memos" +msgstr "In danh sách các ghi nhớ" + +#: ../ui/evolution-memos.xml.h:14 +msgid "View the selected memo" +msgstr "Xem ghi nhớ đã chọn" + +#: ../ui/evolution-memos.xml.h:18 +msgid "_Open Memo" +msgstr "_Mở ghi nhớ" + +#: ../ui/evolution-message-composer.xml.h:2 +msgid "Attach a file" +msgstr "Đính kèm tập tin" + +#: ../ui/evolution-signature-editor.xml.h:13 +msgid "Close the current file" +msgstr "Đóng tập tin hiện thời" + +#: ../ui/evolution-message-composer.xml.h:5 +#: ../ui/evolution-message-composer.xml.h:6 +msgid "Delete all but signature" +msgstr "Xoá bỏ toàn thứ trừ chữ ký" + +#: ../ui/evolution-message-composer.xml.h:6 +#: ../ui/evolution-message-composer.xml.h:7 +msgid "Encrypt this message with PGP" +msgstr "Mật mã hóa thư này, dùng PGP" + +#: ../ui/evolution-message-composer.xml.h:7 +#: ../ui/evolution-message-composer.xml.h:8 +msgid "Encrypt this message with your S/MIME Encryption Certificate" +msgstr "Mật mã hoá thư này, dùng Chứng nhận Mật mã hóa S/MIME của bạn" + +#: ../src/widgets/cria-main-window.c:1581 +msgid "For_mat" +msgstr "_Định dạng" + +#: ../ui/evolution-message-composer.xml.h:9 +msgid "Get delivery notification when your message is read" +msgstr "Chọn để nhận thông báo khi người nhận đã đọc thư bạn" + +#: ../ui/evolution-message-composer.xml.h:10 +msgid "HT_ML" +msgstr "HT_ML" + +#: ui/galeon-ui.xml.in.h:94 ../libgnomeui/gnome-app-helper.c:109 +msgid "Open a file" +msgstr "Mở tập tin" + +#: ../ui/evolution-message-composer.xml.h:13 +msgid "PGP Encrypt" +msgstr "Mật mã hóa PGP" + +#: ../ui/evolution-message-composer.xml.h:14 +msgid "PGP Sign" +msgstr "Chữ ký PGP" + +#: ../ui/evolution-message-composer.xml.h:15 +msgid "R_equest Read Receipt" +msgstr "_Yêu cầu thông báo đã đọc" + +#: ../ui/evolution-message-composer.xml.h:16 +msgid "S/MIME Encrypt" +msgstr "Mật mã hoá S/MIME" + +#: ../ui/evolution-message-composer.xml.h:17 +msgid "S/MIME Sign" +msgstr "Chữ ký S/MIME" + +#: ui/galeon-ui.xml.in.h:120 +msgid "Save As" +msgstr "Lưu dạng" + +#: ../ui/evolution-message-composer.xml.h:20 +msgid "Save Draft" +msgstr "Lưu nháp" + +#: ../libgnomeui/gnome-app-helper.c:119 ../app/actions/file-actions.c:91 +msgid "Save _As..." +msgstr "Lưu _dạng..." + +#: ../ui/evolution-message-composer.xml.h:22 ../src/main.c:623 +msgid "Save _Draft" +msgstr "Lưu _nháp" + +#: ../ui/evolution-message-composer.xml.h:23 +msgid "Save as draft" +msgstr "Lưu dạng nháp" + +#: ../ui/evolution-message-composer.xml.h:24 +msgid "Save in folder..." +msgstr "Lưu vào thư mục..." + +#: ../ui/evolution-message-composer.xml.h:25 +msgid "Save the current file" +msgstr "Lưu tập tin hiện thời" + +#: ../ui/evolution-message-composer.xml.h:26 +msgid "Save the current file with a different name" +msgstr "Lưu tập tin hiện thời với tên khác" + +#: ../ui/evolution-message-composer.xml.h:27 +msgid "Save the message in a specified folder" +msgstr "Lưu thông điệp vào thư mục xác định" + +#: ../ui/evolution-signature-editor.xml.h:11 +msgid "Send the mail in HTML format" +msgstr "Gởi thông điệp với dạng thức HTML" + +#: ../ui/evolution-message-composer.xml.h:31 +msgid "Set the message priority to high" +msgstr "Đặt ưu tiên thư là cao" + +#: ../ui/evolution-message-composer.xml.h:32 +#: ../ui/evolution-message-composer.xml.h:31 +msgid "Sign this message with your PGP key" +msgstr "Ký tên vào thư này, dùng khoá PGP của bạn" + +#: ../ui/evolution-message-composer.xml.h:33 +#: ../ui/evolution-message-composer.xml.h:32 +msgid "Sign this message with your S/MIME Signature Certificate" +msgstr "Ký tên vào thư này, dùng Chứng nhận Chữ ký S/MIME của bạn" + +#: ../ui/evolution-message-composer.xml.h:34 +#: ../ui/evolution-message-composer.xml.h:33 +msgid "Toggles whether the BCC field is displayed" +msgstr "Bật tắt hiển thị trường Bí mật Chép Cho (BCC)" + +#: ../ui/evolution-message-composer.xml.h:35 +#: ../ui/evolution-message-composer.xml.h:34 +msgid "Toggles whether the CC field is displayed" +msgstr "Bật tắt hiển thị trường Chép Cho (CC)" + +#: ../ui/evolution-message-composer.xml.h:36 +#: ../ui/evolution-message-composer.xml.h:35 +msgid "Toggles whether the From chooser is displayed" +msgstr "Bật tắt hiển thị bộ chọn From (Từ)" + +#: ../ui/evolution-message-composer.xml.h:37 +#: ../ui/evolution-message-composer.xml.h:36 +msgid "Toggles whether the Post-To field is displayed" +msgstr "Bật tắt hiển thị trường Post-To (Gởi cho nhóm tin tức)" + +#: ../ui/evolution-message-composer.xml.h:38 +#: ../ui/evolution-message-composer.xml.h:37 +msgid "Toggles whether the Reply-To field is displayed" +msgstr "Bật tắt hiển thị trường Reply-To (Trả lời)" + +#: ../ui/evolution-message-composer.xml.h:39 +#: ../ui/evolution-message-composer.xml.h:38 +msgid "Toggles whether the To field is displayed" +msgstr "Bật tắt hiển thị trường To (Cho)" + +#: ../ui/evolution-message-composer.xml.h:41 +#: ../ui/evolution-message-composer.xml.h:40 +msgid "_Bcc Field" +msgstr "Trường _BCC" + +#: ../ui/evolution-message-composer.xml.h:42 +#: ../ui/evolution-message-composer.xml.h:41 +msgid "_Cc Field" +msgstr "Trường _CC" + +#: ../ui/evolution-message-composer.xml.h:44 +#: ../ui/evolution-message-composer.xml.h:43 +msgid "_Delete all" +msgstr "_Xoá bỏ tất cả" + +#: ../ui/evolution-message-composer.xml.h:47 +#: ../ui/evolution-message-composer.xml.h:46 +msgid "_From Field" +msgstr "Trường _From (Từ)" + +#: ../app/actions/file-actions.c:71 ../src/Actions.cs:52 app/menubar.c:413 +msgid "_Open..." +msgstr "_Mở..." + +#: ../ui/evolution-message-composer.xml.h:50 +#: ../ui/evolution-message-composer.xml.h:49 +msgid "_Post-To Field" +msgstr "Trường _Post-To (Gởi cho nhóm tin tức)" + +#: ../ui/evolution-message-composer.xml.h:51 +msgid "_Prioritise Message" +msgstr "_Ưu tiên hóa thư" + +#: ../ui/evolution-message-composer.xml.h:52 +#: ../ui/evolution-message-composer.xml.h:50 +msgid "_Reply-To Field" +msgstr "Trường _Reply-To (Trả lời)" + +#: ../ui/evolution-message-composer.xml.h:54 +#: ../ui/evolution-message-composer.xml.h:52 +msgid "_Security" +msgstr "_Bảo mật" + +#: ../ui/evolution-message-composer.xml.h:55 +#: ../ui/evolution-message-composer.xml.h:53 +msgid "_To Field" +msgstr "Trường To (Cho)" + +#: ../ui/evolution-signature-editor.xml.h:1 +msgid "C_lose" +msgstr "Đón_g" + +#: ../ui/evolution-signature-editor.xml.h:15 +#: ../ui/evolution-signature-editor.xml.h:5 +msgid "H_TML" +msgstr "H_TML" + +#: ../ui/evolution-signature-editor.xml.h:16 +#: ../ui/evolution-signature-editor.xml.h:7 +msgid "Save and Close" +msgstr "Lưu và Đóng" + +#: ../ui/evolution-signature-editor.xml.h:20 +msgid "Save and _Close" +msgstr "Lưu và Đón_g" + +#: ../ui/evolution-signature-editor.xml.h:21 +#: ../ui/evolution-signature-editor.xml.h:10 +msgid "Save the current file and close the window" +msgstr "Lưu tập tin hiện thời và đóng cửa sổ" + +#: ../ui/evolution-subscribe.xml.h:1 +msgid "Add folder to your list of subscribed folders" +msgstr "Thêm thư mục vào danh sách những thư mục đăng ký" + +#: ../ui/evolution-subscribe.xml.h:3 +msgid "Refresh List" +msgstr "Cập nhật danh sách" + +#: ../ui/evolution-subscribe.xml.h:4 +msgid "Refresh List of Folders" +msgstr "Cập nhật danh sách các thư mục" + +#: ../ui/evolution-subscribe.xml.h:5 +msgid "Remove folder from your list of subscribed folders" +msgstr "Gỡ bỏ thư mục khỏi danh sách các thư mục đã đăng ký" + +#: ../ui/evolution-subscribe.xml.h:7 ../extensions/rss/rss-ui.c:591 +#: ../glade/straw.glade.h:60 ../src/lib/subscribe.py:173 +msgid "Subscribe" +msgstr "Đăng ký" + +#: ../ui/evolution-subscribe.xml.h:8 +msgid "Unsubscribe" +msgstr "Bỏ đăng ký" + +#: ../ui/evolution-subscribe.xml.h:12 +msgid "_Invert Selection" +msgstr "_Đảo vùng chọn" + +#: ../ui/evolution-task-editor.xml.h:4 +msgid "Click change / view the status details of the task" +msgstr "Nhấn để thay đổi/xem chi tiết trạng thái của tác vụ" + +#: ../ui/evolution-task-editor.xml.h:10 +msgid "Status Details" +msgstr "Chi tiết trạng thái" + +#: ../ui/evolution-task-editor.xml.h:11 +msgid "Time Zone" +msgstr "Múi giờ" + +#: ../ui/evolution-task-editor.xml.h:23 +msgid "_Status Details" +msgstr "_Chi tiết trạng thái" + +#: ../ui/evolution-tasks.xml.h:3 +msgid "Copy selected task" +msgstr "Chép tác vụ đã chọn" + +#: ../ui/evolution-tasks.xml.h:5 +msgid "Cut selected task" +msgstr "Cắt tác vụ đã chọn" + +#: ../ui/evolution-tasks.xml.h:7 +msgid "Delete completed tasks" +msgstr "Xoá bỏ mọi tác vụ hoàn tất" + +#: ../ui/evolution-tasks.xml.h:8 +msgid "Delete selected tasks" +msgstr "Xoá bỏ các tác vụ được chọn" + +#: ../ui/evolution-tasks.xml.h:9 +msgid "Mar_k as Complete" +msgstr "Nhãn _hoàn tất" + +#: ../ui/evolution-tasks.xml.h:10 +msgid "Mark selected tasks as complete" +msgstr "Đánh dấu các tác vụ được chọn là hoàn tất" + +#: ../ui/evolution-tasks.xml.h:12 +msgid "Paste task from the clipboard" +msgstr "Dán tác vụ từ bảng tạm" + +#: ../ui/evolution-tasks.xml.h:13 +msgid "Previews the list of tasks to be printed" +msgstr "Xem thử danh sách các tác vụ cần in" + +#: ../ui/evolution-tasks.xml.h:16 +msgid "Print the list of tasks" +msgstr "In danh sách các tác vụ" + +#: ../ui/evolution-tasks.xml.h:18 +msgid "Show task preview window" +msgstr "Hiện khung xem thử tác vụ" + +#: ../ui/evolution-tasks.xml.h:19 +msgid "Task _Preview" +msgstr "_Xem thử tác vụ" + +#: ../ui/evolution-tasks.xml.h:20 ../ui/evolution-tasks.xml.h:18 +msgid "View the selected task" +msgstr "Xem tác vụ được chọn" + +#: ../ui/evolution-tasks.xml.h:27 ../ui/evolution-tasks.xml.h:25 +msgid "_Open Task" +msgstr "_Mở tác vụ" + +#: ../ui/evolution.xml.h:1 +msgid "About Evolution..." +msgstr "Giới thiệu về Evolution..." + +#: ../ui/evolution.xml.h:2 +msgid "Change Evolution's settings" +msgstr "Đổi thiết lập Evolution" + +#: ../ui/evolution.xml.h:3 +msgid "Change the visibility of the toolbar" +msgstr "Hiện/Ẩn thanh công cụ" + +#: ../ui/evolution.xml.h:5 +msgid "Create a new window displaying this folder" +msgstr "Tạo cửa sổ mới hiển thị thư mục này" + +#: ../ui/evolution.xml.h:6 +msgid "Display window buttons using the desktop toolbar setting" +msgstr "" +"Hiển thị mọi cái nút cửa sổ dùng thiết lập thanh công cụ của màn hình nền" + +#: ../ui/evolution.xml.h:7 +msgid "Display window buttons with icons and text" +msgstr "Hiển thị mọi cái nút cửa sổ dùng ảnh và chữ đều" + +#: ../ui/evolution.xml.h:8 +msgid "Display window buttons with icons only" +msgstr "Hiển thị mọi cái nút cửa sổ dùng chỉ ảnh thôi" + +#: ../ui/evolution.xml.h:9 +msgid "Display window buttons with text only" +msgstr "Hiển thị mọi cái nút cửa sổ dùng chỉ chữ thôi" + +#: ../ui/evolution.xml.h:10 src/ui.cc:1450 src/ui.cc:1385 +#: ../src/ghex-ui.xml.h:20 +msgid "Exit the program" +msgstr "Thoát khỏi chương trình" + +#: ../ui/evolution.xml.h:11 ../ui/evolution.xml.h:12 +msgid "Forget _Passwords" +msgstr "Quên các _mật khẩu" + +#: ../ui/evolution.xml.h:12 ../ui/evolution.xml.h:13 +msgid "Forget remembered passwords so you will be prompted for them again" +msgstr "" +"Quên đi các mật khẩu đã nhớ, như vậy bạn sẽ lại được nhắc nhập mật khẩu" + +#: ../ui/evolution.xml.h:13 ../ui/evolution.xml.h:14 +msgid "Hide window buttons" +msgstr "Ẩn mọi nút cửa sổ" + +#: ../ui/evolution.xml.h:14 ../ui/evolution.xml.h:15 +msgid "Icons _and text" +msgstr "Ảnh _và chữ" + +#: ../ui/evolution.xml.h:15 ../ui/evolution.xml.h:16 +msgid "Import data from other programs" +msgstr "Nhập dữ liệu từ chương trình khác" + +#: ../ui/evolution.xml.h:17 +msgid "New _Window" +msgstr "Cửa sổ mớ_i" + +#: ../ui/evolution.xml.h:18 ../shell/rb-shell.c:386 +msgid "Prefere_nces" +msgstr "Tù_y thích" + +#: ../ui/evolution.xml.h:19 ../ui/evolution.xml.h:20 +msgid "Send / Receive" +msgstr "Gởi / Nhận" + +#: ../ui/evolution.xml.h:20 +msgid "Send / _Receive" +msgstr "Gởi / _Nhận" + +#: ../ui/evolution.xml.h:21 +msgid "Send queued items and retrieve new items" +msgstr "Gởi các mục đang đợi gởi và nhận các mục mới" + +#: ../ui/evolution.xml.h:22 +msgid "Set up Pilot configuration" +msgstr "Thiết lập cấu hình Pilot" + +#: ../ui/evolution.xml.h:23 +msgid "Show information about Evolution" +msgstr "Hiện thông tin về Evolution" + +#: ../ui/evolution.xml.h:24 +msgid "Submit Bug Report" +msgstr "Gởi báo cáo lỗi" + +#: ../ui/evolution.xml.h:29 ../ui/evolution.xml.h:25 +msgid "Submit _Bug Report" +msgstr "_Gởi báo cáo lỗi" + +#: ../ui/evolution.xml.h:30 ../ui/evolution.xml.h:26 +msgid "Submit a bug report using Bug Buddy" +msgstr "Báo cáo lỗi, dùng Bug Buddy" + +#: ../ui/evolution.xml.h:31 ../ui/evolution.xml.h:27 +msgid "Toggle whether we are working offline." +msgstr "Bật tắt hoạt động ngoại tuyến" + +#: ../ui/evolution.xml.h:32 ../ui/evolution.xml.h:28 +msgid "Tool_bar" +msgstr "_Thanh công cụ" + +#: ../ui/evolution.xml.h:33 ../ui/evolution.xml.h:29 +msgid "Tool_bar style" +msgstr "_Kiểu thanh công cụ" + +#: ../ui/evolution.xml.h:34 +msgid "View/Hide the Status Bar" +msgstr "Xem/Ẩn thanh trạng thái" + +#: ../ui/evolution.xml.h:35 ../ui/evolution.xml.h:30 +msgid "_About Evolution..." +msgstr "_Giới thiệu Evolution..." + +#: ../ui/evolution.xml.h:36 src/fe-gtk/menu.c:1828 +msgid "_Close Window" +msgstr "_Đóng cửa sổ" + +#: ../ui/evolution.xml.h:40 ../ui/evolution.xml.h:35 +msgid "_Hide buttons" +msgstr "Ẩ_n nút" + +#: ../ui/evolution.xml.h:41 ../ui/evolution.xml.h:36 +msgid "_Icons only" +msgstr "_Chỉ ảnh thôi" + +#: ../ui/evolution.xml.h:42 ../ui/evolution.xml.h:37 ../src/f-spot.glade.h:167 +#: src/interface.c:517 src/interface.c:168 +msgid "_Import..." +msgstr "_Nhập..." + +#: ../ui/evolution.xml.h:44 ../ui/evolution.xml.h:39 +msgid "_Quick Reference" +msgstr "_Tham khảo nhanh" + +#: ../ui/evolution.xml.h:46 ../ui/evolution.xml.h:42 +msgid "_Switcher Appearance" +msgstr "Hình thức bộ _chuyển đổi" + +#: ../ui/evolution.xml.h:47 ../ui/evolution.xml.h:43 +msgid "_Synchronization Options..." +msgstr "Tùy chọn _đồng bộ..." + +#: ../ui/evolution.xml.h:48 ../ui/evolution.xml.h:44 +msgid "_Text only" +msgstr "Chỉ _chữ thôi" + +#: ../ui/evolution.xml.h:50 +msgid "_View Status Bar" +msgstr "_Xem thanh trạng thái" + +#: ../ui/evolution.xml.h:51 src/fe-gtk/menu.c:1305 ../ui/evolution.xml.h:46 +#: ../gnopi/gnopi_files/Find/find.glade2.h:35 src/fe-gtk/menu.c:1438 +msgid "_Window" +msgstr "_Cửa sổ" + +#: ../views/addressbook/galview.xml.h:1 +msgid "By _Company" +msgstr "Theo Công t_y" + +#: ../views/addressbook/galview.xml.h:2 +msgid "_Address Cards" +msgstr "_Thẻ địa chỉ" + +#: ../views/addressbook/galview.xml.h:3 +msgid "_Phone List" +msgstr "Danh sách điện th_oại" + +#: ../views/calendar/galview.xml.h:1 +msgid "W_eek View" +msgstr "Khung _tuần" + +#: ../views/calendar/galview.xml.h:2 +msgid "_Day View" +msgstr "Khung n_gày" + +#: ../views/calendar/galview.xml.h:3 +msgid "_List View" +msgstr "Khung xem _danh sách" + +#: ../views/calendar/galview.xml.h:4 +msgid "_Month View" +msgstr "Khung t_háng" + +#: ../views/calendar/galview.xml.h:5 +msgid "_Work Week View" +msgstr "Khung _tuần làm việc" + +#: ../views/mail/galview.xml.h:1 +msgid "As _Sent Folder" +msgstr "Theo _thư mục gởi" + +#: ../views/mail/galview.xml.h:2 +msgid "By S_tatus" +msgstr "Theo t_rạng thái" + +#: ../views/mail/galview.xml.h:3 +msgid "By Se_nder" +msgstr "Theo người _gởi" + +#: ../views/mail/galview.xml.h:4 +msgid "By Su_bject" +msgstr "Theo _chủ đề" + +#: ../views/mail/galview.xml.h:5 +msgid "By _Follow Up Flag" +msgstr "Theo _cờ theo dõi tiếp" + +#: ../views/mail/galview.xml.h:6 po/silky.glade.h:217 +#, fuzzy +msgid "_Messages" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"_Thư\n" +"#-#-#-#-# silky-0.5.3pre1.vi.po (silky-0.5.3pre1) #-#-#-#-#\n" +"Tin _nhẳn" + +#: ../views/memos/galview.xml.h:1 +msgid "_Memos" +msgstr "Ghi _nhớ" + +#: ../views/tasks/galview.xml.h:1 +msgid "With _Due Date" +msgstr "Với ngày đến _hạn" + +#: ../views/tasks/galview.xml.h:2 +msgid "With _Status" +msgstr "Với _trạng thái" + +#: ../Sensors/Clock/__init__.py:48 ../src/util.c:301 +msgid "UTC" +msgstr "Giờ thế giới" + +#: ../widgets/e-timezone-dialog/e-timezone-dialog.glade.h:2 +msgid "Time Zones" +msgstr "Múi giờ" + +#: ../widgets/e-timezone-dialog/e-timezone-dialog.glade.h:3 +msgid "_Selection" +msgstr "Điều _chọn" + +#: ../widgets/e-timezone-dialog/e-timezone-dialog.glade.h:5 +msgid "Select a Time Zone" +msgstr "Chọn múi giờ" + +#: ../widgets/e-timezone-dialog/e-timezone-dialog.glade.h:6 +msgid "TimeZone Combobox" +msgstr "Hộp tổ hợp múi giờ" + +#: ../widgets/e-timezone-dialog/e-timezone-dialog.glade.h:7 +msgid "" +"Use the left mouse button to zoom in on an area of the map and select a time " +"zone.\n" +"Use the right mouse button to zoom out." +msgstr "" +"Hãy dùng nút chuột trái để phóng to vùng trên bản đồ và chọn múi giờ.\n" +"Dùng nút chuột phải để thu nhỏ." + +#: gal/menus/gal-define-views-model.c:185 +msgid "Collection" +msgstr "Tập hợp" + +#: gal/menus/gal-define-views.glade.h:4 +#, no-c-format +msgid "Define Views for %s" +msgstr "Định nghĩa khung xem cho « %s »" + +#: gal/menus/gal-define-views-dialog.c:322 +msgid "Define Views" +msgstr "Định nghĩa khung xem" + +#: gal/menus/gal-define-views.glade.h:2 +#, no-c-format +msgid "Define Views for \"%s\"" +msgstr "Định nghĩa khung xem cho « %s »" + +#: ../providers/odbc/gda-odbc-provider.c:1271 ../widgets/gtk+.xml.in.h:178 +#: ../src/form-editor/widget-util.cc:224 ../src/orca/rolenames.py:423 +msgid "Table" +msgstr "Bảng" + +#: ../widgets/menus/gal-view-instance-save-as-dialog.c:182 +#: gal/menus/gal-view-instance-save-as-dialog.c:183 +msgid "Instance" +msgstr "tức thời" + +#: ../widgets/menus/gal-view-instance-save-as-dialog.c:232 +#: gal/menus/gal-view-instance-save-as-dialog.c:229 +msgid "Save Current View" +msgstr "Lưu khung xem hiện có" + +#: ../widgets/menus/gal-view-instance-save-as-dialog.glade.h:3 +#: gal/menus/gal-view-instance-save-as-dialog.glade.h:3 +msgid "_Create new view" +msgstr "Tạo khung xem _mới" + +#: ../widgets/menus/gal-view-instance-save-as-dialog.glade.h:5 +#: gal/menus/gal-view-instance-save-as-dialog.glade.h:5 +msgid "_Replace existing view" +msgstr "Tha_y thế khung xem đã có" + +#: ../widgets/menus/gal-view-instance.c:585 gal/menus/gal-view-instance.c:574 +msgid "Custom View" +msgstr "Khung xem tự chọn" + +#: ../widgets/menus/gal-view-instance.c:587 ../calendar/gui/gnome-cal.c:2210 +#: ../widgets/menus/gal-view-instance.c:586 gal/menus/gal-view-instance.c:575 +msgid "Save Custom View" +msgstr "Lưu khung xem tự chọn" + +#: ../widgets/menus/gal-view-instance.c:590 gal/menus/gal-view-instance.c:579 +msgid "Define Views..." +msgstr "Định nghĩa khung xem" + +#: ../widgets/menus/gal-view-menus.c:359 +msgid "Save Custom View..." +msgstr "Lưu khung xem tự chọn..." + +#: ../widgets/menus/gal-view-new-dialog.c:80 +#: gal/menus/gal-view-new-dialog.c:77 ../sheets/IsometricMap.sheet.in.h:14 +msgid "Factory" +msgstr "Bộ tạo" + +#: ../widgets/menus/gal-view-new-dialog.c:115 +#: gal/menus/gal-view-new-dialog.c:108 +msgid "Define New View" +msgstr "Định nghĩa khung xem mới" + +#: ../widgets/menus/gal-view-new-dialog.glade.h:2 +#: gal/menus/gal-view-new-dialog.glade.h:2 +msgid "Name of new view:" +msgstr "Tên khung xem mới:" + +#: ../widgets/menus/gal-view-new-dialog.glade.h:3 +msgid "Type of View" +msgstr "Loại khung xem" + +#: ../widgets/menus/gal-view-new-dialog.glade.h:4 +#: gal/menus/gal-view-new-dialog.glade.h:3 +msgid "Type of view:" +msgstr "Loại khung xem:" + +#. Translators: These are the first characters of each day of the +#. week, 'M' for 'Monday', 'T' for Tuesday etc. +#: ../widgets/misc/e-calendar-item.c:415 +msgid "MTWTFSS" +msgstr "HBTNSBC" + +#: ../widgets/misc/e-calendar-item.c:1099 ../gncal/calendar-month.c:204 +msgid "%B %Y" +msgstr "%B %Y" + +#: ../widgets/misc/e-calendar.c:177 ../widgets/misc/e-calendar.c:201 +msgid "Previous Button" +msgstr "Nút trước" + +#: ../widgets/misc/e-calendar.c:226 +msgid "Month Calendar" +msgstr "Lịch tháng" + +#: ../lib/properties.h:503 ../lib/properties.c:75 ../lib/properties.h:499 +#: ../lib/properties.h:502 +msgid "Fill color" +msgstr "Màu tô" + +#: gal/e-text/e-text.c:3531 gal/e-text/e-text.c:3538 gal/e-text/e-text.c:3539 +msgid "GDK fill color" +msgstr "Màu tô đầy GDK" + +#: gal/e-text/e-text.c:3546 +msgid "Fill stipple" +msgstr "Tô đầy dùng thuật vẽ bằng chấm" + +#: ../widgets/misc/e-canvas-background.c:484 +#: ../widgets/misc/e-canvas-background.c:485 +msgid "X1" +msgstr "X1" + +#: ../widgets/misc/e-canvas-background.c:491 +#: ../widgets/misc/e-canvas-background.c:492 +msgid "X2" +msgstr "X2" + +#: ../widgets/misc/e-canvas-background.c:498 +#: ../widgets/misc/e-canvas-background.c:499 +msgid "Y1" +msgstr "Y1" + +#: ../widgets/misc/e-canvas-background.c:505 +#: ../widgets/misc/e-canvas-background.c:506 +msgid "Y2" +msgstr "Y2" + +#: gal/e-table/e-table-group-container.c:983 +msgid "Minimum width" +msgstr "Độ rộng tối thiểu" + +#: gal/e-table/e-table-group-container.c:984 ../gtk/gtktreeviewcolumn.c:279 +msgid "Minimum Width" +msgstr "Độ rộng tối thiểu" + +#: ../gtk/gtktreeviewcolumn.c:251 ../app/widgets/gimpgrideditor.c:242 +#: ../app/dia-props.c:136 +msgid "Spacing" +msgstr "Khoảng cách" + +#: ../widgets/misc/e-cell-date-edit.c:233 ../widgets/misc/e-dateedit.c:460 +#: ../widgets/misc/e-cell-date-edit.c:232 ../widgets/misc/e-dateedit.c:451 +#: ../src/personal_info.c:269 +msgid "Now" +msgstr "Bây giờ" + +#: ../widgets/misc/e-cell-date-edit.c:802 +#: ../widgets/misc/e-cell-date-edit.c:801 +#, c-format +msgid "The time must be in the format: %s" +msgstr "Thời gian phải theo dạng thức: %s" + +#: ../widgets/misc/e-cell-percent.c:76 +msgid "The percent value must be between 0 and 100, inclusive" +msgstr "Giá trị phần trăm phải nằm giữa 0 và 100, kể cả hai số đó" + +#: ../widgets/misc/e-charset-picker.c:63 ../pan/pan-charset-picker.c:38 +#: ../pan/pan-charset-picker.c:39 ../pan/pan-charset-picker.c:40 +msgid "Baltic" +msgstr "Ban-tích" + +#: ../pan/pan-charset-picker.c:42 +msgid "Central European" +msgstr "Trung Âu" + +#: ../src/util.c:394 +msgid "Chinese" +msgstr "Trung Quốc" + +#: ../pan/pan-charset-picker.c:46 ../pan/pan-charset-picker.c:47 +msgid "Cyrillic" +msgstr "Ki-rin" + +#: ../pan/pan-charset-picker.c:49 +msgid "Greek" +msgstr "Hy-lạp" + +#: ../src/languages.c:153 ../src/util.c:408 +msgid "Hebrew" +msgstr "Do-thái" + +#: ../pan/pan-charset-picker.c:50 +msgid "Japanese" +msgstr "Nhật-bản" + +#: ../pan/pan-charset-picker.c:51 +msgid "Korean" +msgstr "Hàn Quốc" + +#: ../src/languages.c:297 ../src/util.c:436 ../pan/pan-charset-picker.c:52 +msgid "Turkish" +msgstr "Thổ-nhĩ-kỳ" + +#: src/galeon-prefs-dialog.c:647 +msgid "Unicode" +msgstr "Unicode" + +#: ../widgets/misc/e-charset-picker.c:73 +msgid "Western European" +msgstr "Tây Âu" + +#: ../widgets/misc/e-charset-picker.c:74 +msgid "Western European, New" +msgstr "Tây Âu, Mới" + +#: ../desktop-themes/Traditional/index.theme.in.h:1 +msgid "Traditional" +msgstr "Truyền thống" + +#: ../widgets/misc/e-charset-picker.c:94 ../widgets/misc/e-charset-picker.c:95 +#: ../widgets/misc/e-charset-picker.c:96 ../widgets/misc/e-charset-picker.c:97 +msgid "Simplified" +msgstr "Đơn giản" + +#: ../widgets/misc/e-charset-picker.c:100 ../src/languages.c:301 +#: ../src/util.c:437 +msgid "Ukrainian" +msgstr "U-cợ-rainh" + +#: ../widgets/misc/e-charset-picker.c:103 +msgid "Visual" +msgstr "Trực quan" + +#: ../widgets/misc/e-charset-picker.c:171 +#, c-format +msgid "Unknown character set: %s" +msgstr "Bộ ký tự lạ: %s" + +#: ../widgets/misc/e-charset-picker.c:216 +#: ../widgets/misc/e-charset-picker.c:479 +msgid "Character Encoding" +msgstr "Mã ký tự" + +#: ../widgets/misc/e-charset-picker.c:231 +msgid "Enter the character set to use" +msgstr "Hãy gõ bộ ký tự cần dùng" + +#: ../src/f-spot.glade.h:4 +msgid "..." +msgstr "..." + +#: ../widgets/misc/e-dateedit.c:320 ../widgets/misc/e-dateedit.c:316 +msgid "Date and Time Entry" +msgstr "Nhập Ngày và Giờ" + +#: ../widgets/misc/e-dateedit.c:339 ../widgets/misc/e-dateedit.c:335 +msgid "Text entry to input date" +msgstr "Trường nhập để gõ ngày" + +#: ../widgets/misc/e-dateedit.c:340 ../widgets/misc/e-dateedit.c:336 +msgid "Text Date Entry" +msgstr "Chỗ gõ ngày" + +#: ../widgets/misc/e-dateedit.c:361 ../widgets/misc/e-dateedit.c:353 +msgid "Click this button to show a calendar" +msgstr "Nhắp vào cái nút này để hiển thị một lịch" + +#: ../widgets/misc/e-dateedit.c:362 ../widgets/misc/e-dateedit.c:354 +msgid "Date Button" +msgstr "Nút ngày" + +#: ../widgets/misc/e-dateedit.c:383 ../widgets/misc/e-dateedit.c:374 +msgid "Combo box to select time" +msgstr "Hộp tổ hợp để chọn giờ" + +#: ../widgets/misc/e-dateedit.c:384 ../widgets/misc/e-dateedit.c:375 +msgid "Time Combo Box" +msgstr "Hộp tổ hợp giờ" + +#: ../gncal/utils-time.c:48 +msgid "%H:%M" +msgstr "%H:%M" + +#: ../applets/clock/clock.c:1725 +msgid "%I:%M %p" +msgstr "%I:%M %p" + +#: ../widgets/misc/e-expander.c:181 ../gtk/gtkexpander.c:198 +msgid "Expanded" +msgstr "Đã mở rộng" + +#: ../widgets/misc/e-expander.c:182 +msgid "Whether or not the expander is expanded" +msgstr "Có bung mũi tên bung hay không" + +#: ../widgets/misc/e-expander.c:190 +msgid "Text of the expander's label" +msgstr "Chữ trong nhãn mũi tên bung" + +#: ../widgets/misc/e-expander.c:197 +msgid "Use underline" +msgstr "Dùng gạch chân" + +#: ../widgets/misc/e-expander.c:198 +msgid "" +"If set, an underline in the text indicates the next character should be used " +"for the mnemonic accelerator key" +msgstr "Nếu lập thì gạch chân ngú ý ký tự sau nó là phím tắt." + +#: ../glade/gbwidgets/gbexpander.c:58 +msgid "Space to put between the label and the child" +msgstr "Số điểm ảnh giữa nhãn và con." + +#: ../gtk/gtkframe.c:170 ../gtk/gtktoolbutton.c:201 +msgid "Label widget" +msgstr "Ô điều khiển nhãn" + +#: ../widgets/misc/e-expander.c:216 +msgid "A widget to display in place of the usual expander label" +msgstr "Một ô điều khiển để hiển thị trong chỗ nhãn của mũi tên bung thường" + +#: ../gtk/gtktreeview.c:716 +msgid "Expander Size" +msgstr "Cỡ mũi tên bung" + +#: ../gtk/gtktreeview.c:717 +msgid "Size of the expander arrow" +msgstr "Cỡ mũi tên bung" + +#: ../widgets/misc/e-expander.c:231 +msgid "Indicator Spacing" +msgstr "Dấu cách chỉ báo" + +#: ../widgets/misc/e-expander.c:232 +msgid "Spacing around expander arrow" +msgstr "Dấu cách ở quanh mũi tên bung" + +#: ../widgets/misc/e-filter-bar.c:201 ../widgets/misc/e-filter-bar.c:195 +msgid "_Searches" +msgstr "Việc tìm _kiếm" + +#: ../widgets/misc/e-filter-bar.c:203 +msgid "Searches" +msgstr "Việc tìm kiếm" + +#. FIXME: get the toplevel window... +#: ../widgets/misc/e-filter-bar.c:226 ../widgets/misc/e-filter-bar.c:220 +msgid "Save Search" +msgstr "Lưu việc tìm kiếm" + +#: ../widgets/misc/e-filter-bar.h:92 ../widgets/misc/e-filter-bar.h:99 +msgid "_Save Search..." +msgstr "_Lưu việc tìm kiếm..." + +#: ../widgets/misc/e-filter-bar.h:93 ../widgets/misc/e-filter-bar.h:100 +msgid "_Edit Saved Searches..." +msgstr "_Sửa đổi việc tìm kiếm đã lưu..." + +#: ../widgets/misc/e-filter-bar.h:94 ../widgets/misc/e-filter-bar.h:101 +msgid "_Advanced Search..." +msgstr "Tìm kiếm cấp c_ao" + +#: ../widgets/misc/e-image-chooser.c:172 +msgid "Choose Image" +msgstr "Chọn ảnh" + +#: ../widgets/misc/e-map.c:651 ../widgets/misc/e-map.c:647 +msgid "World Map" +msgstr "Bản đồ thế giới" + +#: ../widgets/misc/e-map.c:653 ../widgets/misc/e-map.c:649 +msgid "" +"Mouse-based interactive map widget for selecting timezone. Keyboard users " +"should select the timezone from the below combo box instead." +msgstr "" +"Ô điều khiển bản đồ tương tác đựa vào con chuột để chọn múi giờ. Người thích " +"dùng bàn phím thì nên chọn múi giờ trong hộp tổ hợp bên dưới thay vào đó." + +#: ../widgets/misc/e-pilot-settings.c:103 +msgid "Sync with:" +msgstr "Đồng bộ hóa với:" + +#: ../widgets/misc/e-pilot-settings.c:111 +msgid "Sync Private Records:" +msgstr "Đồng bộ hóa mục ghi riêng:" + +#: ../widgets/misc/e-pilot-settings.c:120 +msgid "Sync Categories:" +msgstr "Phân loại đồng bộ :" + +#: ../widgets/misc/e-reflow.c:1452 ../widgets/misc/e-reflow.c:1453 +msgid "Empty message" +msgstr "Thư rỗng" + +#: ../widgets/misc/e-reflow.c:1459 ../widgets/misc/e-reflow.c:1460 +msgid "Reflow model" +msgstr "Mẫu thông lượng lại" + +#: ../widgets/misc/e-reflow.c:1466 ../widgets/misc/e-reflow.c:1467 +msgid "Column width" +msgstr "Rộng cột" + +#: ../widgets/misc/e-search-bar.c:357 ../widgets/misc/e-search-bar.c:345 +msgid "Search Text Entry" +msgstr "Chỗ gõ chữ tìm kiếm" + +#: ../Tomboy/NoteWindow.cs:419 +msgid "_Search" +msgstr "Tìm _kiếm" + +#: ../widgets/misc/e-search-bar.c:582 ../widgets/misc/e-search-bar.c:555 +#: ../storage/sunone-subscription-dialog.glade.h:2 +msgid "_Find Now" +msgstr "Tìm n_gay" + +#: ../src/menus.c:60 ../gtk/gtkstock.c:322 ../plug-ins/gfig/gfig-dialog.c:880 +#: ../src/Actions.cs:96 +msgid "_Clear" +msgstr "_Xoá" + +#: ../widgets/misc/e-search-bar.c:674 ../widgets/misc/e-search-bar.c:647 +msgid "Search Type" +msgstr "Kiểu tìm kiếm" + +#: ../widgets/misc/e-search-bar.c:878 ../widgets/misc/e-search-bar.c:851 +msgid "Item ID" +msgstr "ID mục" + +#: ../widgets/misc/e-search-bar.c:885 ../widgets/misc/e-search-bar.c:858 +msgid "Subitem ID" +msgstr "ID mục con" + +#: ../widgets/misc/e-search-bar.c:972 ../widgets/misc/e-search-bar.c:945 +msgid "Find _Now" +msgstr "Tìm _ngay" + +#: gal/widgets/e-selection-model-array.c:543 +msgid "Cursor Row" +msgstr "Hàng con trỏ" + +#: gal/widgets/e-selection-model-array.c:550 +msgid "Cursor Column" +msgstr "Cột con trỏ" + +#: ../widgets/misc/e-selection-model.c:214 gal/widgets/e-selection-model.c:210 +msgid "Sorter" +msgstr "Bộ sắp xếp" + +#: ../widgets/misc/e-selection-model.c:221 gal/widgets/e-selection-model.c:217 +msgid "Selection Mode" +msgstr "Chế độ lựa chọn" + +#: ../widgets/misc/e-selection-model.c:229 gal/widgets/e-selection-model.c:225 +msgid "Cursor Mode" +msgstr "Chế độ con trỏ" + +#: ../widgets/misc/e-send-options.c:524 ../widgets/misc/e-send-options.c:521 +msgid "When de_leted:" +msgstr "Khi _xoá bỏ :" + +#: ../widgets/misc/e-send-options.glade.h:1 +msgid "Delivery Options" +msgstr "Tùy chọn gởi" + +#: ../widgets/misc/e-send-options.glade.h:2 +msgid "Replies" +msgstr "Trả lời" + +#: ../widgets/misc/e-send-options.glade.h:3 +msgid "Return Notification" +msgstr "Trở về thông báo" + +#: ../widgets/misc/e-send-options.glade.h:4 +msgid "Status Tracking" +msgstr "Theo dõi trạng thái" + +#: ../widgets/misc/e-send-options.glade.h:5 +msgid "A_uto-delete sent item" +msgstr "_Tự động xoá bỏ mục đã gởi" + +#: ../widgets/misc/e-send-options.glade.h:6 +msgid "C_lassification" +msgstr "_Phân loại" + +#: ../widgets/misc/e-send-options.glade.h:7 +msgid "Creat_e a sent item to track information" +msgstr "Tạ_o mục đã gởi để theo dõi thông tin" + +#: ../widgets/misc/e-send-options.glade.h:8 +msgid "Deli_vered and opened" +msgstr "Đã _phát và mở" + +#: ../widgets/misc/e-send-options.glade.h:9 +msgid "Gene_ral Options" +msgstr "Tùy chọn ch_ung" + +#: ../widgets/misc/e-send-options.glade.h:10 +msgid "" +"None\n" +"Mail Receipt" +msgstr "" +"Không có\n" +"Thông báo đã đọc" + +#: ../widgets/misc/e-send-options.glade.h:12 +msgid "" +"Public\n" +"Private\n" +"Confidential\n" +msgstr "" +"Công\n" +"Riêng\n" +"Tin tưởng\n" + +#: ../widgets/misc/e-send-options.glade.h:16 +msgid "R_eply requested" +msgstr "Yêu cầu t_rả lời" + +#: ../widgets/misc/e-send-options.glade.h:18 +msgid "Sta_tus Tracking" +msgstr "Theo dõi _trạng thái" + +#: ../widgets/misc/e-send-options.glade.h:19 +msgid "" +"Undefined\n" +"High\n" +"Standard\n" +"Low" +msgstr "" +"Chưa định nghĩa\n" +"Cao\n" +"Chuẩn\n" +"Thấp" + +#: ../widgets/misc/e-send-options.glade.h:23 +msgid "W_ithin" +msgstr "Ở tr_ong" + +#: ../widgets/misc/e-send-options.glade.h:24 +msgid "When acce_pted:" +msgstr "Khi chấ_p nhận" + +#: ../widgets/misc/e-send-options.glade.h:25 +msgid "When co_mpleted:" +msgstr "Khi _hoàn tất:" + +#: ../widgets/misc/e-send-options.glade.h:26 +msgid "When decli_ned:" +msgstr "Khi bị từ chối:" + +#: ../widgets/misc/e-send-options.glade.h:27 +msgid "_After:" +msgstr "_Sau :" + +#: ../widgets/misc/e-send-options.glade.h:28 +msgid "_All information" +msgstr "_Mọi thông tin" + +#: ../widgets/misc/e-send-options.glade.h:29 +msgid "_Delay message delivery" +msgstr "Gởi t_rễ thư" + +#: ../widgets/misc/e-send-options.glade.h:30 +msgid "_Delivered" +msgstr "Đã _phát" + +#: ../widgets/misc/e-send-options.glade.h:32 +msgid "_Set expiration date" +msgstr "_Lập ngày hết hạn" + +#: ../widgets/misc/e-send-options.glade.h:33 +msgid "_Until:" +msgstr "_Đến khi:" + +#: ../widgets/misc/e-send-options.glade.h:34 +msgid "_When convenient" +msgstr "Khi _tiện" + +#: ../widgets/misc/e-send-options.glade.h:35 +msgid "_When opened:" +msgstr "Khi _mở :" + +# Variable: do not translate/ biến: đừng dịch +#: ../widgets/misc/e-task-widget.c:208 +#, c-format +msgid "%s (...)" +msgstr "%s (...)" + +#: ../widgets/misc/e-task-widget.c:213 +#, c-format +msgid "%s (%d%% complete)" +msgstr "%s (%d%% hoàn tất)" + +#: ../widgets/misc/e-url-entry.c:107 +msgid "click here to go to url" +msgstr "nhấn đây để đi tới địa chỉ Mạng" + +#: ../widgets/misc/gal-categories.glade.h:2 +#: gal/widgets/gal-categories.glade.h:2 +msgid "Edit Master Category List..." +msgstr "Sửa đổi danh sách phân loại chính..." + +#: ../widgets/misc/gal-categories.glade.h:3 +#: gal/widgets/gal-categories.glade.h:3 +msgid "Item(s) belong to these _categories:" +msgstr "Mục thuộc các loại này:" + +#: ../widgets/misc/gal-categories.glade.h:4 +#: gal/widgets/gal-categories.glade.h:4 +msgid "_Available Categories:" +msgstr "_Loại sẵn có :" + +#: ../widgets/misc/gal-categories.glade.h:5 +#: gal/widgets/gal-categories.glade.h:5 +msgid "categories" +msgstr "loại" + +#: ../widgets/table/e-cell-combo.c:177 +msgid "popup list" +msgstr "danh sách bật lên" + +#: ../widgets/table/e-cell-date.c:64 gal/e-table/e-cell-date.c:58 +#: ../applets/clock/clock.c:266 ../applets/clock/clock.c:591 +msgid "%l:%M %p" +msgstr "%l:%M %p" + +#: ../widgets/table/e-cell-pixbuf.c:397 gal/e-table/e-cell-pixbuf.c:392 +msgid "Selected Column" +msgstr "Cột đã chọn" + +#: ../widgets/table/e-cell-pixbuf.c:404 gal/e-table/e-cell-pixbuf.c:399 +msgid "Focused Column" +msgstr "Cột có tiêu điểm" + +#: ../widgets/table/e-cell-pixbuf.c:411 gal/e-table/e-cell-pixbuf.c:406 +msgid "Unselected Column" +msgstr "Cột đã bỏ chọn" + +#: ../widgets/table/e-cell-text.c:1740 gal/e-table/e-cell-text.c:1703 +msgid "Strikeout Column" +msgstr "Cột gạch ngang" + +#: ../widgets/table/e-cell-text.c:1747 gal/e-table/e-cell-text.c:1710 +msgid "Underline Column" +msgstr "Cột gạch dưới" + +#: ../widgets/table/e-cell-text.c:1754 gal/e-table/e-cell-text.c:1717 +msgid "Bold Column" +msgstr "Cột đậm" + +#: ../widgets/table/e-cell-text.c:1761 gal/e-table/e-cell-text.c:1724 +msgid "Color Column" +msgstr "Cột màu" + +#: ../widgets/table/e-cell-text.c:1775 gal/e-table/e-cell-text.c:1738 +msgid "BG Color Column" +msgstr "Cột màu nền" + +#: gal/e-table/e-table-config.glade.h:1 +msgid "<- _Remove" +msgstr "← _Gỡ bỏ" + +#: gal/e-table/e-table-config.glade.h:2 +msgid "A_vailable Fields:" +msgstr "Trường có _sẵn:" + +#: gal/e-table/e-table-config.glade.h:3 +msgid "Ascending" +msgstr "Tăng dần" + +#: gal/e-table/e-table-config.glade.h:4 +msgid "Clear All" +msgstr "Xoá hết" + +#: gal/e-table/e-table-config.glade.h:5 +msgid "Descending" +msgstr "Giảm dần" + +#: gal/e-table/e-table-config.glade.h:8 +msgid "Group Items By" +msgstr "Nhóm lại mục theo" + +#: gal/e-table/e-table-config.glade.h:9 +msgid "Move _Down" +msgstr "Chuyển _xuống" + +#: ../libnautilus-private/nautilus-column-chooser.c:394 ../src/cd-lib.c:377 +msgid "Move _Up" +msgstr "Đem _lên" + +#: ../widgets/table/e-table-config-no-group.glade.h:11 +#: gal/e-table/e-table-config-no-group.glade.h:11 +msgid "Sh_ow these fields in order:" +msgstr "_Hiện những trường này theo thứ tự :" + +#: gal/e-table/e-table-config.glade.h:11 +msgid "Show Fields" +msgstr "Hiện trường" + +#: gal/e-table/e-table-config.glade.h:12 +msgid "Show field in View" +msgstr "Hiện trường trong Khung xem" + +#: src/fe-gtk/editlist.c:376 ../gnomecard/gnomecard-main-window.c:656 +#: ../sheets/Flowchart.sheet.in.h:28 +msgid "Sort" +msgstr "Sắp xếp" + +#: gal/e-table/e-table-config.glade.h:14 +msgid "Sort Items By" +msgstr "Sắp xếp mục theo" + +#: gal/e-table/e-table-config.glade.h:15 +msgid "Then By" +msgstr "Rồi theo" + +#: gal/e-table/e-table-config.glade.h:16 +msgid "_Add ->" +msgstr "Th_êm →" + +#: gal/e-table/e-table-config.glade.h:17 +msgid "_Fields Shown..." +msgstr "T_rường đã hiện..." + +#: gal/e-table/e-table-config.glade.h:20 +msgid "_Sort..." +msgstr "_Sắp xếp..." + +#: ../extensions/permissions/ephy-permissions-dialog.c:527 +#: ../sheets/SDL.sheet.in.h:18 ../sheets/UML.sheet.in.h:28 +#: ../providers/sybase/utils.c:357 +msgid "State" +msgstr "Tính trạng" + +#: ../widgets/table/e-table-config.c:307 ../widgets/table/e-table-config.c:349 +#: gal/e-table/e-table-config.c:309 gal/e-table/e-table-config.c:351 +msgid "(Ascending)" +msgstr "(Tăng dần)" + +#: ../widgets/table/e-table-config.c:307 ../widgets/table/e-table-config.c:349 +#: gal/e-table/e-table-config.c:309 gal/e-table/e-table-config.c:351 +msgid "(Descending)" +msgstr "(Giảm dần)" + +#: ../widgets/table/e-table-config.c:314 gal/e-table/e-table-config.c:316 +msgid "Not sorted" +msgstr "Chưa sắp xếp" + +#: ../widgets/table/e-table-config.c:355 gal/e-table/e-table-config.c:357 +msgid "No grouping" +msgstr "Chưa nhóm lại" + +#: ../widgets/table/e-table-config.c:584 +msgid "Available Fields" +msgstr "Trường có sẵn:" + +#: ../widgets/table/e-table-config.glade.h:17 +#: gal/e-table/e-table-config.glade.h:18 +msgid "_Group By..." +msgstr "_Nhóm lại theo..." + +#: ../widgets/table/e-table-config.glade.h:19 +#: gal/e-table/e-table-config.glade.h:19 +msgid "_Show these fields in order:" +msgstr "_Hiển thị những trường này theo thứ tự :" + +#: gal/e-table/e-table-header-item.c:1795 +msgid "DnD code" +msgstr "Mã DnD" + +#: gal/e-table/e-table-header-item.c:1809 +msgid "Full Header" +msgstr "Phần đầu đầy đủ" + +#: ../widgets/table/e-table-field-chooser-dialog.c:126 +#: gal/e-table/e-table-field-chooser-dialog.c:123 +msgid "Add a column..." +msgstr "Thêm cột..." + +#: ../widgets/table/e-table-field-chooser.glade.h:1 +#: gal/e-table/e-table-field-chooser.glade.h:1 +msgid "Field Chooser" +msgstr "Bộ chọn trường" + +#: ../widgets/table/e-table-field-chooser.glade.h:2 +#: gal/e-table/e-table-field-chooser.glade.h:2 +msgid "" +"To add a column to your table, drag it into\n" +"the location in which you want it to appear." +msgstr "" +"Để thêm một cột vào bảng,\n" +"hãy kéo nó vào vị trí đã muốn." + +#: ../widgets/table/e-table-group-container.c:350 +#: gal/e-table/e-table-group-container.c:355 +#, c-format +msgid "%s : %s (%d item)" +msgstr "%s : %s (%d mục)" + +#: ../widgets/table/e-table-group-container.c:351 +#: gal/e-table/e-table-group-container.c:356 +#, c-format +msgid "%s : %s (%d items)" +msgstr "%s : %s (%d mục)" + +#: ../widgets/table/e-table-group-container.c:356 +#: gal/e-table/e-table-group-container.c:361 +#, c-format +msgid "%s (%d item)" +msgstr "%s (%d mục)" + +#: ../widgets/table/e-table-group-container.c:357 +#: gal/e-table/e-table-group-container.c:362 +#, c-format +msgid "%s (%d items)" +msgstr "%s (%d mục)" + +#: gal/e-table/e-table-group-container.c:907 +msgid "Alternating Row Colors" +msgstr "Màu hàng xen kẽ" + +#: gal/e-table/e-tree.c:3268 +msgid "Horizontal Draw Grid" +msgstr "Lưới vẽ ngang" + +#: gal/e-table/e-tree.c:3274 +msgid "Vertical Draw Grid" +msgstr "Lưới vẽ dọc" + +#: gal/e-table/e-tree.c:3280 +msgid "Draw focus" +msgstr "Tiêu điểm vẽ" + +#: gal/e-table/e-table-group-container.c:935 +msgid "Cursor mode" +msgstr "Chế độ con trỏ" + +#: gal/e-table/e-table-group-container.c:942 +msgid "Selection model" +msgstr "Mô hình lựa chọn" + +#: gal/e-table/e-tree.c:3261 gal/e-table/e-tree.c:3262 +msgid "Length Threshold" +msgstr "Ngưỡng dài" + +#: gal/e-table/e-tree.c:3293 gal/e-table/e-tree.c:3294 +msgid "Uniform row height" +msgstr "Độ cao hàng không đổi" + +#: gal/e-table/e-table-group-container.c:963 +msgid "Frozen" +msgstr "Đông cứng" + +#: ../widgets/table/e-table-header-item.c:1472 +#: gal/e-table/e-table-header-item.c:1457 +msgid "Customize Current View" +msgstr "Tùy biến khung xem hiện thời" + +#: ../widgets/table/e-table-header-item.c:1492 +#: gal/e-table/e-table-header-item.c:1477 +msgid "Sort Ascending" +msgstr "Sắp xếp tăng dần" + +#: ../widgets/table/e-table-header-item.c:1493 +#: gal/e-table/e-table-header-item.c:1478 +msgid "Sort Descending" +msgstr "Sắp xếp giảm dần" + +#: ../widgets/table/e-table-header-item.c:1494 +#: gal/e-table/e-table-header-item.c:1479 +msgid "Unsort" +msgstr "Hủy sắp xếp" + +#: ../widgets/table/e-table-header-item.c:1496 +#: gal/e-table/e-table-header-item.c:1481 +msgid "Group By This Field" +msgstr "Nhóm lại theo trường này" + +#: ../widgets/table/e-table-header-item.c:1497 +#: gal/e-table/e-table-header-item.c:1482 +msgid "Group By Box" +msgstr "Nhóm lại theo hộp" + +#: ../widgets/table/e-table-header-item.c:1499 +#: gal/e-table/e-table-header-item.c:1484 +msgid "Remove This Column" +msgstr "Bỏ cột này" + +#: ../widgets/table/e-table-header-item.c:1500 +#: gal/e-table/e-table-header-item.c:1485 +msgid "Add a Column..." +msgstr "Thêm cột..." + +#: ../app/tools/gimpclonetool.c:338 ../glade/gbwidgets/gbalignment.c:255 +#: ../widgets/gtk+.xml.in.h:7 +msgid "Alignment" +msgstr "Canh lề" + +#: gal/e-table/e-table-header-item.c:1488 ../xpdf/gpdf-control-ui.xml.h:2 +msgid "Best Fit" +msgstr "Vừa nhất" + +#: gal/e-table/e-table-header-item.c:1489 +msgid "Format Columns..." +msgstr "Định dạng cột..." + +#: gal/e-table/e-table-header-item.c:1491 +msgid "Customize Current View..." +msgstr "Tùy biến khung xem hiện thời..." + +#: ../widgets/text/e-entry.c:1264 gal/e-table/e-table-header-item.c:1802 +msgid "Fontset" +msgstr "Bộ phông chữ" + +#: gal/e-table/e-table-header-item.c:1823 gal/e-table/e-table-sorter.c:172 +msgid "Sort Info" +msgstr "Sắp xếp thông tin" + +#: ../src/file-manager/fm-tree-view.c:1488 ../src/orca/rolenames.py:473 +msgid "Tree" +msgstr "Cây" + +#: ../plugins/taglist/HTML.tags.xml.in.h:235 +msgid "Table header" +msgstr "Đầu bảng" + +#: ../widgets/table/e-table-item.c:2949 ../widgets/table/e-table-item.c:2950 +#: ../widgets/table/e-table-item.c:2945 ../widgets/table/e-table-item.c:2946 +msgid "Table model" +msgstr "Mẫu bảng" + +#: ../widgets/table/e-table-item.c:3025 ../widgets/table/e-table-item.c:3026 +#: ../widgets/table/e-table-item.c:3021 ../widgets/table/e-table-item.c:3022 +msgid "Cursor row" +msgstr "Hàng con trỏ" + +#: ../widgets/table/e-table.c:3330 gal/e-table/e-table.c:3313 +msgid "Always Search" +msgstr "Luôn tìm kiếm" + +#: ../widgets/table/e-table.c:3337 gal/e-table/e-table.c:3320 +msgid "Use click to add" +msgstr "Nhấn chuột để thêm" + +#: ../widgets/table/e-tree.c:3290 ../widgets/table/e-tree.c:3291 +#: gal/e-table/e-tree.c:3286 gal/e-table/e-tree.c:3287 +msgid "ETree table adapter" +msgstr "Bộ tiếp hợp ETree (bảng cây điện)" + +#: ../widgets/table/e-tree.c:3304 ../widgets/table/e-tree.c:3305 +#: gal/e-table/e-tree.c:3300 gal/e-table/e-tree.c:3301 +msgid "Always search" +msgstr "Luôn tìm kiếm" + +#: ../widgets/table/e-tree.c:3311 gal/e-table/e-tree.c:3307 +msgid "Retro Look" +msgstr "Vẻ cũ" + +#: ../widgets/table/e-tree.c:3312 gal/e-table/e-tree.c:3308 +msgid "Draw lines and +/- expanders." +msgstr "Vẽ đường và mũi tên bung +/-" + +#: ../widgets/text/e-entry-test.c:49 +msgid "Minicard Test" +msgstr "Kiểm tra thẻ tí tị" + +#: ../widgets/text/e-entry-test.c:50 +msgid "Copyright (C) 2000, Helix Code, Inc." +msgstr "Bản quyền © năm 2000, Helix Code, Inc." + +#: ../widgets/text/e-entry-test.c:52 +msgid "This should test the minicard canvas item" +msgstr "Hành động này nên thử ra mục vải căng thẻ tí tị" + +#: gal/e-text/e-text.c:3438 gal/e-text/e-text.c:3439 +msgid "Event Processor" +msgstr "Bộ xử lý sự kiện" + +#: src/prefsdlg.cpp:44 jpilot.c:2620 +msgid "Font" +msgstr "Phông chữ" + +#: ../widgets/text/e-entry.c:1270 ../widgets/text/e-entry.c:1271 +msgid "GDKFont" +msgstr "Phông chữ GDK" + +#: ../gtk/gtktexttag.c:380 ../gtk/gtktextview.c:595 +msgid "Justification" +msgstr "Canh đều" + +#: gal/e-text/e-text.c:3574 gal/e-text/e-text.c:3575 +msgid "Use ellipsis" +msgstr "Dùng dấu chấm lửng" + +#: gal/e-text/e-text.c:3581 gal/e-text/e-text.c:3582 +msgid "Ellipsis" +msgstr "Dấu chấm lửng" + +#: gal/e-text/e-text.c:3588 gal/e-text/e-text.c:3589 ../gtk/gtklabel.c:368 +msgid "Line wrap" +msgstr "Ngắt dòng" + +#: gal/e-text/e-text.c:3595 gal/e-text/e-text.c:3596 +msgid "Break characters" +msgstr "Ngắt ký tự" + +#: gal/e-text/e-text.c:3602 gal/e-text/e-text.c:3603 +msgid "Max lines" +msgstr "Số dòng tối đa" + +#: gal/e-text/e-text.c:3631 gal/e-text/e-text.c:3632 +msgid "Allow newlines" +msgstr "Cho phép ký tự dòng mới" + +#: gal/e-text/e-text.c:3624 gal/e-text/e-text.c:3625 +msgid "Draw borders" +msgstr "Viền vẽ" + +#: gal/e-text/e-text.c:3638 gal/e-text/e-text.c:3639 ../lib/properties.c:76 +#: ../lib/properties.h:505 ../lib/properties.h:508 +msgid "Draw background" +msgstr "Nền vẽ" + +#: gal/e-text/e-text.c:3645 gal/e-text/e-text.c:3646 +msgid "Draw button" +msgstr "Nút vẽ" + +#: gal/e-text/e-text.c:3652 gal/e-text/e-text.c:3653 +msgid "Cursor position" +msgstr "Ví trị con trỏ" + +#: ../widgets/text/e-entry.c:1389 ../widgets/text/e-entry.c:1390 +msgid "Emulate label resize" +msgstr "Mô phỏng đổi cỡ nhãn" + +#: ../widgets/text/e-text.c:2696 gal/e-text/e-text.c:2679 +#: ../components/html-editor/popup.c:553 +msgid "Input Methods" +msgstr "Cách nhập" + +#: ../components/html-editor/toolbar.c:551 +#: ../src/form-editor/button-prop.cc:146 ../src/widgets/font-combo.cc:47 +msgid "Bold" +msgstr "Đậm" + +#: ../components/html-editor/toolbar.c:557 +msgid "Strikeout" +msgstr "Gạch xoá" + +#: ../libgimpwidgets/gimpstock.c:113 +msgid "Anchor" +msgstr "Neo" + +#: ../widgets/text/e-text.c:3498 ../widgets/text/e-text.c:3499 +#: gal/e-text/e-text.c:3481 gal/e-text/e-text.c:3482 +msgid "Clip Width" +msgstr "Rộng trích đoạn" + +#: ../widgets/text/e-text.c:3505 ../widgets/text/e-text.c:3506 +#: gal/e-text/e-text.c:3488 gal/e-text/e-text.c:3489 +msgid "Clip Height" +msgstr "Cao trích đoạn" + +#: ../widgets/text/e-text.c:3512 ../widgets/text/e-text.c:3513 +#: gal/e-text/e-text.c:3495 gal/e-text/e-text.c:3496 +msgid "Clip" +msgstr "Trích đoạn" + +#: ../widgets/text/e-text.c:3519 ../widgets/text/e-text.c:3520 +#: gal/e-text/e-text.c:3502 gal/e-text/e-text.c:3503 +msgid "Fill clip rectangle" +msgstr "Tô đầy hình chữ nhật trích đoạn" + +#: ../widgets/text/e-text.c:3526 ../widgets/text/e-text.c:3527 +#: gal/e-text/e-text.c:3509 gal/e-text/e-text.c:3510 +msgid "X Offset" +msgstr "Hiệu số X" + +#: ../widgets/text/e-text.c:3533 ../widgets/text/e-text.c:3534 +#: gal/e-text/e-text.c:3516 gal/e-text/e-text.c:3517 +msgid "Y Offset" +msgstr "Hiệu số Y" + +#: ../widgets/text/e-text.c:3569 ../widgets/text/e-text.c:3570 +msgid "Text width" +msgstr "Rộng văn bản" + +#: ../widgets/text/e-text.c:3576 ../widgets/text/e-text.c:3577 +msgid "Text height" +msgstr "Cao văn bản" + +#: ../widgets/text/e-text.c:3676 ../widgets/text/e-text.c:3677 +#: gal/e-text/e-text.c:3659 gal/e-text/e-text.c:3660 +msgid "IM Context" +msgstr "Ngữ cảnh IM" + +#: ../widgets/text/e-text.c:3683 ../widgets/text/e-text.c:3684 +#: gal/e-text/e-text.c:3666 gal/e-text/e-text.c:3667 +msgid "Handle Popup" +msgstr "Bộ bật lên móc kéo" + +#: emultempl/armcoff.em:72 +#, c-format +msgid " --support-old-code Support interworking with old code\n" +msgstr " --support-old-code _Hỗ trợ_ dệt vào với _mã cũ_\n" + +#: emultempl/armcoff.em:73 +#, c-format +msgid " --thumb-entry= Set the entry point to be Thumb symbol \n" +msgstr "" +" --thumb-entry= Lập điểm _vào_ là ký hiệu _Hình Nhỏ_Thumb này\n" + +#: emultempl/armcoff.em:121 +#, c-format +msgid "Errors encountered processing file %s" +msgstr "Gặp lỗi khi xử lý tập tin %s" + +#: emultempl/armcoff.em:188 emultempl/pe.em:1455 +msgid "%P: warning: '--thumb-entry %s' is overriding '-e %s'\n" +msgstr "%P: cảnh báo : « --thumb-entry %s » đang lấy quyền cao hơn « -e %s »\n" + +#: emultempl/armcoff.em:193 emultempl/pe.em:1460 +msgid "%P: warning: connot find thumb start symbol %s\n" +msgstr "%P: warning: connot find thumb start symbol %s\n" + +#: emultempl/pe.em:301 +#, c-format +msgid "" +" --base_file Generate a base file for relocatable " +"DLLs\n" +msgstr "" +" --base_file Tạo ra một _tập tin cơ bản_ choocác ata\n" +"\t\t\t\t\t\t\tcó thể định vị lạile DLLs\n" + +#: emultempl/pe.em:302 +#, c-format +msgid "" +" --dll Set image base to the default for DLLs\n" +msgstr "" +" --dll Lập cơ bản ảnh là mặc định cho các DLL\n" + +#: emultempl/pe.em:303 +#, c-format +msgid " --file-alignment Set file alignment\n" +msgstr " --file-alignment Lập cách _canh lề tập tin_\n" + +#: emultempl/pe.em:304 +#, c-format +msgid " --heap Set initial size of the heap\n" +msgstr "" +" --heap Lập kích cỡ _miền nhớ_ ban đầu\n" + +#: emultempl/pe.em:305 +#, c-format +msgid "" +" --image-base
Set start address of the executable\n" +msgstr "" +" --image-base <địa_chỉ> Lập địa chỉ bắt đầu của ứng dụng chạy " +"được\n" +"\t\t\t\t\t\t\t\t (_cơ bản ảnh_)\n" + +#: emultempl/pe.em:306 +#, c-format +msgid "" +" --major-image-version Set version number of the executable\n" +msgstr "" +" --major-image-version \tLập số thứ tự _phiên bản_\n" +"\t\t\t\t\tcủa ứng dụng chạy được (_ảnh lớn_)\n" + +#: emultempl/pe.em:307 +#, c-format +msgid " --major-os-version Set minimum required OS version\n" +msgstr "" +" --major-os-version \t\tLập số thứ tự _phiên bản\n" +"\t\t\t\thệ điều hành_ tối thiểu cần thiết (_lớn_)\n" + +#: emultempl/pe.em:308 +#, c-format +msgid "" +" --major-subsystem-version Set minimum required OS subsystem " +"version\n" +msgstr "" +" --major-subsystem-version \t Lập số thứ tự _phiên bản\n" +"\t\t\t\thệ điều hành con_ tối thiểu cần thiết (_lớn_)\n" + +#: emultempl/pe.em:309 +#, c-format +msgid "" +" --minor-image-version Set revision number of the executable\n" +msgstr "" +" --minor-image-version \tLập số thứ tự bản sửa đổi\n" +"\tcủa ứng dụng chạy được (_phiên bản ảnh nhỏ_)\n" + +#: emultempl/pe.em:310 +#, c-format +msgid " --minor-os-version Set minimum required OS revision\n" +msgstr "" +" --minor-os-version \t\tLập số thứ tự bản sửa đổi\n" +"\t\tcủa hệ điều hành cần thiết (_phiên bản hệ điều hành nhỏ_)\n" + +#: emultempl/pe.em:311 +#, c-format +msgid "" +" --minor-subsystem-version Set minimum required OS subsystem " +"revision\n" +msgstr "" +" --minor-subsystem-version \t Lập số thứ tự bản sửa đổi\n" +"\t\tcủa hệ điều hành con cần thiết (_phiên bản hệ điều hành con nhỏ_)\n" + +#: emultempl/pe.em:312 +#, c-format +msgid " --section-alignment Set section alignment\n" +msgstr " --section-alignment Lập cách _canh lề phần_\n" + +#: emultempl/pe.em:313 +#, c-format +msgid " --stack Set size of the initial stack\n" +msgstr "" +" --stack Lập kích cỡ của _đống_ ban đầu\n" + +#: emultempl/pe.em:314 +#, c-format +msgid "" +" --subsystem [:] Set required OS subsystem [& version]\n" +msgstr "" +" --subsystem [:] Lập _hệ điều hành con_ [và phiên bản] " +"cần thiết\n" + +#: emultempl/pe.em:315 +#, c-format +msgid "" +" --support-old-code Support interworking with old code\n" +msgstr " --support-old-code _Hỗ trợ_ dệt vào với _mã cũ_\n" + +#: emultempl/pe.em:316 +#, c-format +msgid "" +" --thumb-entry= Set the entry point to be Thumb " +"\n" +msgstr " --thumb-entry= Lập điểm _vào_ là ký hiệu _Hình Nhỏ_ này\n" + +#: emultempl/pe.em:318 +#, c-format +msgid "" +" --add-stdcall-alias Export symbols with and without @nn\n" +msgstr "" +" --add-stdcall-alias Xuất ký hiệu với và không với « @nn » (_thêm " +"bí danh gọi chuẩn_)\n" + +#: emultempl/pe.em:319 +#, c-format +msgid " --disable-stdcall-fixup Don't link _sym to _sym@nn\n" +msgstr "" +" --disable-stdcall-fixup Đừng liên kết « _sym » đến « _sym@nn " +"» (_tắt sửa gọi chuẩn_)\n" + +#: emultempl/pe.em:320 +#, c-format +msgid "" +" --enable-stdcall-fixup Link _sym to _sym@nn without warnings\n" +msgstr "" +" --enable-stdcall-fixup Liên kết « _sym » đến « _sym@nn », " +"không có cảnh báo\n" +" \t\t\t\t\t\t\t(_bật sửa gọi chuẩn_)\n" + +#: emultempl/pe.em:321 +#, c-format +msgid "" +" --exclude-symbols sym,sym,... Exclude symbols from automatic export\n" +msgstr "" +" --exclude-symbols ký_hiệu,ký_hiệu,... _Loại trừ những ký hiệu_ này ra " +"việc xuất tự động\n" + +#: emultempl/pe.em:322 +#, c-format +msgid "" +" --exclude-libs lib,lib,... Exclude libraries from automatic " +"export\n" +msgstr "" +" --exclude-libs thư_viên,thư_viên,... _Loại trừ những thư viên_ này " +"ra việc xuất tự động\n" + +#: emultempl/pe.em:323 +#, c-format +msgid "" +" --export-all-symbols Automatically export all globals to " +"DLL\n" +msgstr "" +" --export-all-symbols Tự động _xuất mọi_ điều toàn cục vào " +"DLL (_ký hiệu_)\n" + +#: emultempl/pe.em:324 +#, c-format +msgid " --kill-at Remove @nn from exported symbols\n" +msgstr "" +" --kill-at Gỡ bỏ « @nn » ra những ký hiệu đã xuất " +"(_buộc kết thúc tại_)\n" + +#: emultempl/pe.em:325 +#, c-format +msgid " --out-implib Generate import library\n" +msgstr " --out-implib Tạo _ra thư viên nhập_\n" + +#: emultempl/pe.em:326 +#, c-format +msgid "" +" --output-def Generate a .DEF file for the built DLL\n" +msgstr "" +" --output-def Tạo _ra_ một tập tin .DEF cho DLL đã " +"xây dụng\n" + +#: emultempl/pe.em:327 +#, c-format +msgid " --warn-duplicate-exports Warn about duplicate exports.\n" +msgstr "" +" --warn-duplicate-exports _Cảnh báo_ về _việc xuất trùng_ nào.\n" + +#: emultempl/pe.em:328 +#, c-format +msgid "" +" --compat-implib Create backward compatible import " +"libs;\n" +" create __imp_ as well.\n" +msgstr "" +" --compat-implib Tạo các _thư viên nhập tương thích_ " +"ngược;\n" +"\t\t\t\t\tcũng tạo « __imp_ ».\n" + +#: emultempl/pe.em:330 +#, c-format +msgid "" +" --enable-auto-image-base Automatically choose image base for " +"DLLs\n" +" unless user specifies one\n" +msgstr "" +" --enable-auto-image-base Tự động chọn cơ bản ảnh cho mọi DLL\n" +"\t\t\t\t\t\t\ttrừ khi người dùng gõ nó\n" + +#: emultempl/pe.em:332 +#, c-format +msgid "" +" --disable-auto-image-base Do not auto-choose image base. " +"(default)\n" +msgstr "" +" --disable-auto-image-base Đừng _tự động_ chọn _cơ bản ảnh_ (mặc " +"định) (_tắt_)\n" + +#: emultempl/pe.em:333 +#, c-format +msgid "" +" --dll-search-prefix= When linking dynamically to a dll " +"without\n" +" an importlib, use ." +"dll\n" +" in preference to lib.dll \n" +msgstr "" +" --dll-search-prefix= Khi liên kết động đến DLL không có thư " +"viên nhập,\n" +"\thãy dùng « .dll » hơn « .dll »\n" +"\t(_tiền_tố_tìm_kiếm_)\n" + +#: emultempl/pe.em:336 +#, c-format +msgid "" +" --enable-auto-import Do sophistcated linking of _sym to\n" +" __imp_sym for DATA references\n" +msgstr "" +" --enable-auto-import Liên kết một cách tinh tế\n" +"\t« _sym » đến « __imp_sym » cho các tham chiếu DATA (dữ liệu)\n" +"\t(_bật nhập tự động)\n" + +#: emultempl/pe.em:338 +#, c-format +msgid "" +" --disable-auto-import Do not auto-import DATA items from " +"DLLs\n" +msgstr "" +" --disable-auto-import Đừng _tự động nhập_ mục DATA từ DLL (_tắt_)\n" + +#: emultempl/pe.em:339 +#, c-format +msgid "" +" --enable-runtime-pseudo-reloc Work around auto-import limitations by\n" +" adding pseudo-relocations resolved " +"at\n" +" runtime.\n" +msgstr "" +" --enable-runtime-pseudo-reloc Chỉnh sửa các hạn chế nhập tự động,\n" +"\tbằng cách thêm các việc _định vị lại giả_ được tháo gỡ vào _lúc chạy_. " +"(_bật_)\n" + +#: emultempl/pe.em:342 +#, c-format +msgid "" +" --disable-runtime-pseudo-reloc Do not add runtime pseudo-relocations " +"for\n" +" auto-imported DATA.\n" +msgstr "" +" --disable-runtime-pseudo-reloc Đừng thêm việc _định vị lại giả_\n" +"\tvào _lúc chạy_ cho DATA (dữ liệu) được nhập tự động. (_tắt_)\n" + +#: emultempl/pe.em:344 +#, c-format +msgid "" +" --enable-extra-pe-debug Enable verbose debug output when " +"building\n" +" or linking to DLLs (esp. auto-" +"import)\n" +msgstr "" +" --enable-extra-pe-debug _Bật_ xuất dữ liệu _gỡ lỗi_ chi tiết\n" +"\ttrong khi xây dụng hay liên kết đến DLL nào (nhất là việc tự động nhập) " +"(_thêm_)\n" + +#: emultempl/pe.em:347 +#, c-format +msgid "" +" --large-address-aware Executable supports virtual addresses\n" +" greater than 2 gigabytes\n" +msgstr "" +" --large-address-aware Ứng dụng chạy có hỗ trợ _địa chỉ_ ảo _lớn_ " +"hơn 2 GB\n" +"\t\t\t\t\t\t\t(_kiến thức_)\n" + +#: emultempl/pe.em:414 +msgid "%P: warning: bad version number in -subsystem option\n" +msgstr "" +"%P: cảnh báo : gặp số thứ tự phiên bản sai trong tùy chọn « -subsystem » (hệ " +"thống con)\n" + +#: emultempl/pe.em:445 +msgid "%P%F: invalid subsystem type %s\n" +msgstr "%P%F: kiểu hệ thống con không hợp lệ %s\n" + +#: emultempl/pe.em:484 +msgid "%P%F: invalid hex number for PE parameter '%s'\n" +msgstr "%P%F: số thập lục không hợp lệ cho tham số « %s »\n" + +#: emultempl/pe.em:501 +msgid "%P%F: strange hex info for PE parameter '%s'\n" +msgstr "%P%F: thông tin thập lục lạ cho tham số PE « %s »\n" + +#: emultempl/pe.em:518 +#, c-format +msgid "%s: Can't open base file %s\n" +msgstr "%s: Không thể mở tập tin cơ bản %s\n" + +#: emultempl/pe.em:734 +msgid "%P: warning, file alignment > section alignment.\n" +msgstr "%P: cảnh báo, canh lề tập tin > canh lề phần.\n" + +#: emultempl/pe.em:821 emultempl/pe.em:848 +#, c-format +msgid "Warning: resolving %s by linking to %s\n" +msgstr "Cảnh báo : đang tháo gỡ %s bằng cách liên kết đến %s\n" + +#: emultempl/pe.em:826 emultempl/pe.em:853 +msgid "Use --enable-stdcall-fixup to disable these warnings\n" +msgstr "" +"Hãy dùng « --enable-stdcall-fixup » (bật sửa gọi chuẩn) để tắt các cảnh báo " +"này\n" + +#: emultempl/pe.em:827 emultempl/pe.em:854 +msgid "Use --disable-stdcall-fixup to disable these fixups\n" +msgstr "" +"Hãy dùng « --disable-stdcall-fixup » (tắt sửa gọi chuẩn) để tắt các việc sửa " +"này\n" + +#: emultempl/pe.em:873 +#, c-format +msgid "%C: Cannot get section contents - auto-import exception\n" +msgstr "%C: Không thể lấy nội dung phần: ngoài lệ nhập tự động\n" + +#: emultempl/pe.em:910 +#, c-format +msgid "Info: resolving %s by linking to %s (auto-import)\n" +msgstr "Thông tin: đang tháo gỡ %s bằng cách liên kết đến %s (tự động nhập)\n" + +#: emultempl/pe.em:983 +msgid "%F%P: PE operations on non PE file.\n" +msgstr "%F%P: thao tác PE với tập tin không phải PE.\n" + +#: emultempl/pe.em:1258 +#, c-format +msgid "Errors encountered processing file %s\n" +msgstr "Gặp lỗi trong khi xử lý tập tin %s\n" + +#: emultempl/pe.em:1281 +#, c-format +msgid "Errors encountered processing file %s for interworking" +msgstr "Gặp lỗi trong khi xử lý tập tin %s để dệt vào với nhau" + +#: emultempl/pe.em:1340 ldexp.c:570 ldlang.c:2408 ldlang.c:5135 ldlang.c:5166 +#: ldmain.c:1161 +msgid "%P%F: bfd_link_hash_lookup failed: %E\n" +msgstr "" +"%P%F: « bfd_link_hash_lookup » (bfd liên kết băm tra cứu) thất bại: %E\n" + +#: ldcref.c:153 +msgid "%X%P: bfd_hash_table_init of cref table failed: %E\n" +msgstr "" +"%X%P: « bfd_hash_table_init » (bfd băm bảng khởi động) với bảng cref thất " +"bại: %E\n" + +#: ldcref.c:159 +msgid "%X%P: cref_hash_lookup failed: %E\n" +msgstr "%X%P: việc « cref_hash_lookup » bị lỗi: %E\n" + +#: ldcref.c:225 +#, c-format +msgid "" +"\n" +"Cross Reference Table\n" +"\n" +msgstr "" +"\n" +"Bảng Tham Chiếu Chéo\n" +"\n" + +#: ldcref.c:226 ../plug-ins/common/uniteditor.c:104 +#: ../src/widgets/font-combo.cc:49 +msgid "Symbol" +msgstr "Ký hiệu" + +#: ldcref.c:234 +#, c-format +msgid "File\n" +msgstr "Tập tin\n" + +#: ldcref.c:238 +#, c-format +msgid "No symbols\n" +msgstr "Không có ký hiệu\n" + +#: ldcref.c:359 ldcref.c:478 +msgid "%B%F: could not read symbols; %E\n" +msgstr "%B%F: không thể đọc các ký hiệu ; %E\n" + +#: ldcref.c:363 ldcref.c:482 ldmain.c:1226 ldmain.c:1230 +msgid "%B%F: could not read symbols: %E\n" +msgstr "%B%F: không thể đọc các ký hiệu : %E\n" + +#: ldcref.c:414 +msgid "%P: symbol `%T' missing from main hash table\n" +msgstr "%P: thiếu ký hiệu « %T » trong bảng băm chính\n" + +#: ldcref.c:547 ldcref.c:554 ldmain.c:1273 ldmain.c:1280 +msgid "%B%F: could not read relocs: %E\n" +msgstr "%B%F: không thể đọc các điều định vị lại : %E\n" + +#: ldcref.c:573 +msgid "%X%C: prohibited cross reference from %s to `%T' in %s\n" +msgstr "%X%C: không cho phép tham chiếu chéo từ %s đến « %T » trong %s\n" + +#: ldctor.c:84 +msgid "%P%X: Different relocs used in set %s\n" +msgstr "%P%X: Sử dụng sự định vị lại khác nhau trong tập hợp %s\n" + +#: ldctor.c:102 +msgid "%P%X: Different object file formats composing set %s\n" +msgstr "%P%X: Có gồm khuôn dạng tập tin đối tượng khác nhau trong %s\n" + +#: ldctor.c:281 ldctor.c:295 +msgid "%P%X: %s does not support reloc %s for set %s\n" +msgstr "%P%X: %s không hỗ trợ định vị lại %s cho tập hợp %s\n" + +#: ldctor.c:316 +msgid "%P%X: Unsupported size %d for set %s\n" +msgstr "%P%X: Không hỗ trợ kích cỡ %d cho tập hợp %s\n" + +#: ldctor.c:337 +msgid "" +"\n" +"Set Symbol\n" +"\n" +msgstr "" +"\n" +"Tập hợp Ký hiệu\n" +"\n" + +#: ldemul.c:227 +#, c-format +msgid "%S SYSLIB ignored\n" +msgstr "%S SYSLIB bị bỏ qua\n" + +#: ldemul.c:233 +#, c-format +msgid "%S HLL ignored\n" +msgstr "%S HLL bị bỏ qua\n" + +#: ldemul.c:253 +msgid "%P: unrecognised emulation mode: %s\n" +msgstr "%P: không nhận ra chế độ mô phỏng: %s\n" + +#: ldemul.c:254 +msgid "Supported emulations: " +msgstr "Mô phỏng đã hỗ trợ :" + +#: ldemul.c:296 +#, c-format +msgid " no emulation specific options.\n" +msgstr " không có tùy chọn đặc trưng cho mô phỏng.\n" + +#: ldexp.c:379 +#, c-format +msgid "%F%S %% by zero\n" +msgstr "%F%S %% cho số không\n" + +#: ldexp.c:386 +#, c-format +msgid "%F%S / by zero\n" +msgstr "%F%S / cho số không\n" + +#: ldexp.c:583 +#, c-format +msgid "%X%S: unresolvable symbol `%s' referenced in expression\n" +msgstr "" +"%X%S: ký hiệu không tháo gỡ được « %s » được tham chiếu trong biểu thức\n" + +#: ldexp.c:604 +#, c-format +msgid "%F%S: undefined symbol `%s' referenced in expression\n" +msgstr "%F%S: ký hiệu chưa định nghĩa « %s » được tham chiếu trong biểu thức\n" + +#: ldexp.c:665 ldexp.c:678 +#, c-format +msgid "%F%S: undefined MEMORY region `%s' referenced in expression\n" +msgstr "" +"%F%S: miền MEMORY (nhớ) chưa định nghĩa « %s » được tham chiếu trong biểu " +"thức\n" + +#: ldexp.c:757 +#, c-format +msgid "%F%S can not PROVIDE assignment to location counter\n" +msgstr "%F%S không thể PROVIDE (cung cấp) việc gán cho bộ đếm địa điểm\n" + +#: ldexp.c:770 +#, c-format +msgid "%F%S invalid assignment to location counter\n" +msgstr "%F%S việc gán không hợp lệ cho bộ đếm địa điểm\n" + +#: ldexp.c:774 +#, c-format +msgid "%F%S assignment to location counter invalid outside of SECTION\n" +msgstr "" +"%F%S việc gán cho bộ đếm địa điểm không phải hợp lệ bên ngoài SECTION " +"(phần)\n" + +#: ldexp.c:783 +msgid "%F%S cannot move location counter backwards (from %V to %V)\n" +msgstr "%F%S không thể chạy ngược bộ đếm địa điểm (từ %V về %V)\n" + +#: ldexp.c:810 +msgid "%P%F:%s: hash creation failed\n" +msgstr "%P%F:%s: việc tạo băm bị lỗi\n" + +#: ldexp.c:1077 ldexp.c:1109 +#, c-format +msgid "%F%S nonconstant expression for %s\n" +msgstr "%F%S biểu thức thay đổi cho %s\n" + +#: ldexp.c:1163 +#, c-format +msgid "%F%S non constant expression for %s\n" +msgstr "%F%S biểu thức thay đổi cho %s\n" + +#: ldfile.c:139 +#, c-format +msgid "attempt to open %s failed\n" +msgstr "việc cố mở %s bị lỗi\n" + +#: ldfile.c:141 +#, c-format +msgid "attempt to open %s succeeded\n" +msgstr "việc cố mở %s đã thành công\n" + +#: ldfile.c:147 +msgid "%F%P: invalid BFD target `%s'\n" +msgstr "%F%P: đích BFD không hợp lệ « %s »\n" + +#: ldfile.c:255 ldfile.c:282 +msgid "%P: skipping incompatible %s when searching for %s\n" +msgstr "%P: đang nhảy qua %s không tương thích trong khi tìm kiếm %s\n" + +#: ldfile.c:267 +msgid "%F%P: attempted static link of dynamic object `%s'\n" +msgstr "%F%P: đã cố liên kết tĩnh đối tượng động « %s »\n" + +#: ldfile.c:384 +msgid "%F%P: %s (%s): No such file: %E\n" +msgstr "%F%P: %s (%s): Không có tập tin như vậy: %E\n" + +#: ldfile.c:387 +msgid "%F%P: %s: No such file: %E\n" +msgstr "%F%P: %s: Không có tập tin như vậy: %E\n" + +#: ldfile.c:417 +msgid "%F%P: cannot find %s inside %s\n" +msgstr "%F%P: không tìm thấy được %s ở trong %s\n" + +#: ldfile.c:420 +msgid "%F%P: cannot find %s\n" +msgstr "%F%P: không tìm thấy được %s\n" + +#: ldfile.c:437 ldfile.c:453 +#, c-format +msgid "cannot find script file %s\n" +msgstr "không tìm thấy tập tin tập lệnh %s\n" + +#: ldfile.c:439 ldfile.c:455 +#, c-format +msgid "opened script file %s\n" +msgstr "đã mở tập tin tập lệnh %s\n" + +#: ldfile.c:499 +msgid "%P%F: cannot open linker script file %s: %E\n" +msgstr "%P%F: không thể mở tập tin tập lệnh liên kết %s: %E\n" + +#: ldfile.c:546 +msgid "%P%F: cannot represent machine `%s'\n" +msgstr "%P%F: không thể miêu tả máy « %s »\n" + +#: ldlang.c:511 +msgid "%P%F: out of memory during initialization" +msgstr "%P%F: hết bộ nhớ trong khi cài đặt" + +#: ldlang.c:551 +msgid "%P:%S: warning: redeclaration of memory region '%s'\n" +msgstr "%P:%S: cảnh báo : miền nhớ « %s » được khai báo lại\n" + +#: ldlang.c:557 +msgid "%P:%S: warning: memory region %s not declared\n" +msgstr "%P:%S: cảnh báo : chưa khai báo miền bộ nhớ %s\n" + +#: ldlang.c:1073 +msgid "" +"\n" +"Memory Configuration\n" +"\n" +msgstr "" +"\n" +"Cấu hình bộ nhớ\n" +"\n" + +#: ../plug-ins/metadata/interface.c:347 ../providers/sybase/utils.c:475 +msgid "Origin" +msgstr "Gốc" + +#: ../gtk/gtkcellrenderertext.c:235 ../gtk/gtklabel.c:329 +#: ../objects/UML/class.c:205 ../objects/UML/object.c:154 +#: ../src/mlview-icon-tree.cc:1148 +msgid "Attributes" +msgstr "Thuộc tính" + +#: ldlang.c:1115 +#, c-format +msgid "" +"\n" +"Linker script and memory map\n" +"\n" +msgstr "" +"\n" +"Tập lệnh liên kết và bản đồ bộ nhớ\n" +"\n" + +#: ldlang.c:1183 +msgid "%P%F: Illegal use of `%s' section\n" +msgstr "%P%F: Không cho phép cách sử dụng phần « %s »\n" + +#: ldlang.c:1193 +msgid "%P%F: output format %s cannot represent section called %s\n" +msgstr "%P%F: khuôn dạng %s không thể miêu tả phần được gọi là %s\n" + +#: ldlang.c:1775 +msgid "%B: file not recognized: %E\n" +msgstr "%B: không nhận ra tập tin: %E\n" + +#: ldlang.c:1776 +msgid "%B: matching formats:" +msgstr "%B: các dạng thức khớp với nhau :" + +#: ldlang.c:1783 +msgid "%F%B: file not recognized: %E\n" +msgstr "%F%B: không nhận ra tập tin: %E\n" + +#: ldlang.c:1847 +msgid "%F%B: member %B in archive is not an object\n" +msgstr "%F%B: bộ phạn kho %B không phải là đối tượng\n" + +#: ldlang.c:1858 ldlang.c:1872 +msgid "%F%B: could not read symbols: %E\n" +msgstr "%F%B: không thể đọc các ký hiệu : %E\n" + +#: ldlang.c:2127 +msgid "" +"%P: warning: could not find any targets that match endianness requirement\n" +msgstr "" +"%P: cảnh báo : không tìm thấy đích nào khớp với kiểu endian đã cần thiết\n" + +#: ldlang.c:2141 +msgid "%P%F: target %s not found\n" +msgstr "%P%F: không tìm thấy đích %s\n" + +#: ldlang.c:2143 +msgid "%P%F: cannot open output file %s: %E\n" +msgstr "%P%F: không thể mở tập tin xuất %s: %E\n" + +#: ldlang.c:2149 +msgid "%P%F:%s: can not make object file: %E\n" +msgstr "%P%F:%s: không thể tạo tập tin đối tượng: %E\n" + +#: ldlang.c:2153 +msgid "%P%F:%s: can not set architecture: %E\n" +msgstr "%P%F:%s: không thể lập kiến trúc: %E\n" + +#: ldlang.c:2157 +msgid "%P%F: can not create link hash table: %E\n" +msgstr "%P%F: không thể tạo bảng băm liên kết: %E\n" + +#: ldlang.c:2301 +msgid "%P%F: bfd_hash_lookup failed creating symbol %s\n" +msgstr "" +"%P%F: việc « bfd_hash_lookup » (bfd băm tra cứu) bị lỗi, tạo ký hiệu %s\n" + +#: ldlang.c:2319 +msgid "%P%F: bfd_hash_allocate failed creating symbol %s\n" +msgstr "" +"%P%F: « bfd_hash_allocate » (bfd băm cấp cho) thất bại, tạo ký hiệu %s\n" + +#: ldlang.c:2710 +msgid " load address 0x%V" +msgstr " tải địa chỉ 0x%V" + +#: ldlang.c:2874 +msgid "%W (size before relaxing)\n" +msgstr "%W (kích cỡ trước khi lơi ra)\n" + +#: ldlang.c:2961 +#, c-format +msgid "Address of section %s set to " +msgstr "Địa chỉ của phần %s được lập thành " + +#: ldlang.c:3114 +#, c-format +msgid "Fail with %d\n" +msgstr "Thất bại với %d\n" + +#: ldlang.c:3351 +msgid "%X%P: section %s [%V -> %V] overlaps section %s [%V -> %V]\n" +msgstr "%X%P: phần %s [%V → %V] đè lên phần %s [%V → %V]\n" + +#: ldlang.c:3379 +msgid "%X%P: address 0x%v of %B section %s is not within region %s\n" +msgstr "%X%P: địa chỉ 0x%v cửa %B phần %s không phải ở trong miền %s\n" + +#: ldlang.c:3388 +msgid "%X%P: region %s is full (%B section %s)\n" +msgstr "%X%P: miền %s đầy (%B phần %s)\n" + +#: ldlang.c:3439 +msgid "%P%X: Internal error on COFF shared library section %s\n" +msgstr "%P%X: Lỗi nội bộ trên phần thư viên dùng chung COFF %s\n" + +#: ldlang.c:3493 +msgid "%P%F: error: no memory region specified for loadable section `%s'\n" +msgstr "%P%F: lỗi: chưa ghi rõ miền bộ nhớ cho phần tải được « %s »\n" + +#: ldlang.c:3498 +msgid "%P: warning: no memory region specified for loadable section `%s'\n" +msgstr "%P: lỗi: chưa ghi rõ miền bộ nhớ cho phần tải được « %s »\n" + +#: ldlang.c:3515 +msgid "%P: warning: changing start of section %s by %u bytes\n" +msgstr "%P: cảnh báo : đang thay đổi đầu phần %s bằng %u byte\n" + +#: ldlang.c:3532 +#, c-format +msgid "" +"%F%S: non constant or forward reference address expression for section %s\n" +msgstr "%F%S: biểu thức địa chỉ tham chiếu thay đổi hay tiếp lên %s\n" + +#: ldlang.c:3703 +msgid "%P%F: can't relax section: %E\n" +msgstr "%P%F: không thể lơi ra phần: %E\n" + +#: ldlang.c:3960 +msgid "%F%P: invalid data statement\n" +msgstr "%F%P: câu dữ liệu không hợp lệ\n" + +#: ldlang.c:3999 +msgid "%F%P: invalid reloc statement\n" +msgstr "%F%P: câu định vị lại không hợp lệ\n" + +#: ldlang.c:4141 +msgid "%P%F:%s: can't set start address\n" +msgstr "%P%F:%s: không thể lập địa chỉ đầu\n" + +#: ldlang.c:4154 ldlang.c:4173 +msgid "%P%F: can't set start address\n" +msgstr "%P%F: không thể lập địa chỉ đầu\n" + +#: ldlang.c:4166 +msgid "%P: warning: cannot find entry symbol %s; defaulting to %V\n" +msgstr "" +"%P: cảnh báo : không tìm thấy được ký hiệu vào %s; nên dùng mặc định %V\n" + +#: ldlang.c:4178 +msgid "%P: warning: cannot find entry symbol %s; not setting start address\n" +msgstr "" +"%P: cảnh báo : không tìm thấy ký hiệu vào %s; nên không lập địa chỉ bắt đầu " +"symbol %s; not setting start address\n" + +#: ldlang.c:4227 +msgid "" +"%P%F: Relocatable linking with relocations from format %s (%B) to format %s " +"(%B) is not supported\n" +msgstr "" +"%P%F: Không hỗ trợ liên kết định vị lại đưọc có định vị lại từ khuôn dạng %s " +"(%B) sang khuôn dạng %s (%B)\n" + +#: ldlang.c:4237 +msgid "" +"%P: warning: %s architecture of input file `%B' is incompatible with %s " +"output\n" +msgstr "" +"%P: cảnh báo : kiến trức %s của tập tin nhập « %B » không tương thích với dữ " +"liệu xuất %s\n" + +#: ldlang.c:4259 +msgid "%P%X: failed to merge target specific data of file %B\n" +msgstr "%P%X: lỗi hợp nhất dữ liệu đặc trưng cho dữ liệu của tập tin %B\n" + +#: ldlang.c:4343 +msgid "" +"\n" +"Allocating common symbols\n" +msgstr "" +"\n" +"Đang cấp phát các ký hiệu dùng chung\n" + +#: ldlang.c:4344 +msgid "" +"Common symbol size file\n" +"\n" +msgstr "" +"Ký hiệu cùng dùng cỡ tập tin\n" +"\n" + +#: ldlang.c:4470 +msgid "%P%F: invalid syntax in flags\n" +msgstr "%P%F: cụ pháp không hợp lệ trong các cờ\n" + +#: ldlang.c:4740 +msgid "%P%F: Failed to create hash table\n" +msgstr "%P%F: Việc tạo bảng băm bị lỗi\n" + +#: ldlang.c:5057 +msgid "%P%Fmultiple STARTUP files\n" +msgstr "%P%Fcó nhiều tập tin STARTUP (khởi động)\n" + +#: ldlang.c:5105 +msgid "%X%P:%S: section has both a load address and a load region\n" +msgstr "%X%P:%S: phần có cả địa chỉ tải lẫn miền tải đều\n" + +#: ldlang.c:5345 +msgid "%F%P: bfd_record_phdr failed: %E\n" +msgstr "%F%P: việc « bfd_record_phdr » bị lỗi: %E\n" + +#: ldlang.c:5365 +msgid "%X%P: section `%s' assigned to non-existent phdr `%s'\n" +msgstr "%X%P: phần « %s » được gán cho phdr không có « %s »\n" + +#: ldlang.c:5751 +msgid "%X%P: unknown language `%s' in version information\n" +msgstr "%X%P: không biết ngôn ngữ « %s » trong thông tin phiên bản\n" + +#: ldlang.c:5893 +msgid "" +"%X%P: anonymous version tag cannot be combined with other version tags\n" +msgstr "" +"%X%P: thẻ phiên bản vô danh không kết hợp được với thẻ phiên bản khác\n" + +#: ldlang.c:5902 +msgid "%X%P: duplicate version tag `%s'\n" +msgstr "%X%P: thẻ phiên bản trùng « %s »\n" + +#: ldlang.c:5922 ldlang.c:5931 ldlang.c:5948 ldlang.c:5958 +msgid "%X%P: duplicate expression `%s' in version information\n" +msgstr "%X%P: biểu thức trùng « %s » trong thông tin phiên bản\n" + +#: ldlang.c:5998 +msgid "%X%P: unable to find version dependency `%s'\n" +msgstr "%X%P: không tìm thấy được cách phục thuộc vào phiên bản « %s »\n" + +#: ldlang.c:6020 +msgid "%X%P: unable to read .exports section contents\n" +msgstr "%X%P: không thể đọc nội dung của phần « .exports » (xuất)\n" + +#: ldmain.c:229 +msgid "%X%P: can't set BFD default target to `%s': %E\n" +msgstr "%X%P: không thể lập đích mặc định BFD thành « %s »: %E\n" + +#: ldmain.c:341 +msgid "%P%F: --relax and -r may not be used together\n" +msgstr "" +"%P%F: không cho phép sử dụng hai tùy chọn « --relax » (lơi ra) và « -r » với " +"nhau \n" + +#: ldmain.c:343 +msgid "%P%F: -r and -shared may not be used together\n" +msgstr "%P%F: không thể sử dụng cả « -r » lẫn « -shared » (dùng chung) đều\n" + +#: ldmain.c:347 +msgid "%P%F: -static and -shared may not be used together\n" +msgstr "" +"%P%F: không thể sử dụng cả « -static » (tĩnh) lẫn « -shared » (dùng chung) " +"đều\n" + +#: ldmain.c:352 +msgid "%P%F: -F may not be used without -shared\n" +msgstr "" +"%P%F: không thể sử dụng tùy chọn « -F » khi không có tùy chọn « -shared " +"» (dùng chung)\n" + +#: ldmain.c:354 +msgid "%P%F: -f may not be used without -shared\n" +msgstr "" +"%P%F: không thể sử dụng tùy chọn « -f » khi không có tùy chọn « -shared " +"» (dùng chung)\n" + +#: ldmain.c:396 +msgid "using external linker script:" +msgstr "đang dùng tập lệnh liên kết bên ngoài:" + +#: ldmain.c:398 +msgid "using internal linker script:" +msgstr "đang dùng tập lệnh liên kết bên trong:" + +#: ldmain.c:432 +msgid "%P%F: no input files\n" +msgstr "%P%F: không có tập tin nhập nào\n" + +#: ldmain.c:436 +msgid "%P: mode %s\n" +msgstr "%P: chế độ %s\n" + +#: ldmain.c:452 +msgid "%P%F: cannot open map file %s: %E\n" +msgstr "%P%F: không thể mở tập tin bản đồ %s: %E\n" + +#: ldmain.c:482 +msgid "%P: link errors found, deleting executable `%s'\n" +msgstr "%P: tìm thấy một số lỗi liên kết nên xoá bỏ tập tin chạy được « %s »\n" + +#: ldmain.c:491 +msgid "%F%B: final close failed: %E\n" +msgstr "%F%B: việc đóng cuối cùng bị lỗi: %E\n" + +#: ldmain.c:517 +msgid "%X%P: unable to open for source of copy `%s'\n" +msgstr "%X%P: không thể mở cho nguồn của bản sao « %s »\n" + +#: ldmain.c:520 +msgid "%X%P: unable to open for destination of copy `%s'\n" +msgstr "%X%P: không thể mở cho đích của bản sao « %s »\n" + +#: ldmain.c:527 +msgid "%P: Error writing file `%s'\n" +msgstr "%P: Gặp lỗi khi ghi tập tin « %s »\n" + +#: ldmain.c:532 pe-dll.c:1447 +#, c-format +msgid "%P: Error closing file `%s'\n" +msgstr "%P: Gặp lỗi khi đóng tập tin « %s »\n" + +#: ldmain.c:548 +#, c-format +msgid "%s: total time in link: %ld.%06ld\n" +msgstr "%s: thời gian tổng trong liên kết: %ld.%06ld\n" + +#: ldmain.c:551 +#, c-format +msgid "%s: data size %ld\n" +msgstr "%s: kích cỡ dữ liệu %ld\n" + +#: ldmain.c:634 +msgid "%P%F: missing argument to -m\n" +msgstr "%P%F: thiếu đối số tới « -m »\n" + +#: ldmain.c:780 ldmain.c:798 ldmain.c:828 +msgid "%P%F: bfd_hash_table_init failed: %E\n" +msgstr "" +"%P%F: việc « bfd_hash_table_init » (bfd băm bảng khởi động) bị lỗi: %E\n" + +#: ldmain.c:784 ldmain.c:802 +msgid "%P%F: bfd_hash_lookup failed: %E\n" +msgstr "%P%F: việc « bfd_hash_lookup » (tra tìm băm BFD) bị lỗi: %E\n" + +#: ldmain.c:816 +msgid "%X%P: error: duplicate retain-symbols-file\n" +msgstr "%X%P: lỗi: « retain-symbols-file » (giữ lại tập tin ký hiệu) trùng\n" + +#: ldmain.c:858 +msgid "%P%F: bfd_hash_lookup for insertion failed: %E\n" +msgstr "" +"%P%F: việc « bfd_hash_lookup » (bfd băm tra cứu) cho sự chèn bị lỗi: %E\n" + +#: ldmain.c:863 +msgid "%P: `-retain-symbols-file' overrides `-s' and `-S'\n" +msgstr "" +"%P: tùy chọn « -retain-symbols-file » (giữ lại tập tin ký hiệu) đè lên « -s " +"» và « -S »\n" + +#: ldmain.c:938 +#, c-format +msgid "" +"Archive member included because of file (symbol)\n" +"\n" +msgstr "" +"Gồm bộ phạn kho vì tập tin (ký hiệu)\n" +"\n" + +#: ldmain.c:1008 +msgid "%X%C: multiple definition of `%T'\n" +msgstr "%X%C: « %T » đã được định nghĩa nhiều lần\n" + +#: ldmain.c:1011 +msgid "%D: first defined here\n" +msgstr "%D: đã được định nghĩa đầu tiên ở đây\n" + +#: ldmain.c:1015 +msgid "%P: Disabling relaxation: it will not work with multiple definitions\n" +msgstr "" +"%P: Tắt khả năng lơi ra: nó sẽ không hoạt động với nhiều lời định nghĩa\n" + +#: ldmain.c:1045 +msgid "%B: warning: definition of `%T' overriding common\n" +msgstr "%B: cảnh báo : lời định nghĩa « %T » đè lên điều dùng chung\n" + +#: ldmain.c:1048 +msgid "%B: warning: common is here\n" +msgstr "%B: cảnh báo : common (cùng dùng) là đây\n" + +#: ldmain.c:1055 +msgid "%B: warning: common of `%T' overridden by definition\n" +msgstr "%B: cảnh báo : lời định nghĩa đè lên điều cùng dùng của « %T »\n" + +#: ldmain.c:1058 +msgid "%B: warning: defined here\n" +msgstr "%B: cảnh báo : đã được định nghĩa ở đây\n" + +#: ldmain.c:1065 +msgid "%B: warning: common of `%T' overridden by larger common\n" +msgstr "" +"%B: cảnh báo : điều cùng dùng lớn hơn có đè lên điều cùng dùng « %T »\n" + +#: ldmain.c:1068 +msgid "%B: warning: larger common is here\n" +msgstr "%B: cảnh báo : điều dùng chung lớn hơn tại đây\n" + +#: ldmain.c:1072 +msgid "%B: warning: common of `%T' overriding smaller common\n" +msgstr "" +"%B: cảnh báo : điều « %T » dùng chung có đè lên điều dùng chung nhỏ hơn\n" + +#: ldmain.c:1075 +msgid "%B: warning: smaller common is here\n" +msgstr "%B: cảnh báo : điều cùng dùng nhỏ hơn ở đây\n" + +#: ldmain.c:1079 +msgid "%B: warning: multiple common of `%T'\n" +msgstr "%B: cảnh báo : nhiều điều cùng dùng của « %T »\n" + +#: ldmain.c:1081 +msgid "%B: warning: previous common is here\n" +msgstr "%B: cảnh báo : điều cùng dùng trước ở đây\n" + +#: ldmain.c:1101 ldmain.c:1139 +msgid "%P: warning: global constructor %s used\n" +msgstr "%P: cảnh báo : bộ cấu trúc toàn cục %s được dùng\n" + +#: ldmain.c:1149 +msgid "%P%F: BFD backend error: BFD_RELOC_CTOR unsupported\n" +msgstr "%P%F: lỗi hậu phương: « BFD_RELOC_CTOR » không được hỗ trợ\n" + +#: src/xgettext.c:2070 src/complain.c:51 src/complain.c:66 +#, c-format +msgid "warning: " +msgstr "cảnh báo : " + +#: ldmain.c:1327 +msgid "%F%P: bfd_hash_table_init failed: %E\n" +msgstr "" +"%F%P: việc « bfd_hash_table_init » (bfd băm bảng khởi động) bị lỗi: %E\n" + +#: ldmain.c:1334 +msgid "%F%P: bfd_hash_lookup failed: %E\n" +msgstr "%F%P: việc « bfd_hash_lookup » (tra tìm băm BFD) bị lỗi: %E\n" + +#: ldmain.c:1355 +msgid "%X%C: undefined reference to `%T'\n" +msgstr "%X%C: tham chiếu chưa định nghĩa đến « %T »\n" + +#: ldmain.c:1358 +msgid "%C: warning: undefined reference to `%T'\n" +msgstr "%C: cảnh báo : tham chiếu chưa định nghĩa đến « %T »\n" + +#: ldmain.c:1364 +msgid "%X%D: more undefined references to `%T' follow\n" +msgstr "%X%D: có tham chiếu chưa định nghĩa đến « %T » thêm nữa theo sau\n" + +#: ldmain.c:1367 +msgid "%D: warning: more undefined references to `%T' follow\n" +msgstr "" +"%D: chưa định nghĩa lời tham chiếu đến « %T » tại nhiều nơi nữa theo đây\n" + +#: ldmain.c:1378 +msgid "%X%B: undefined reference to `%T'\n" +msgstr "%X%B: tham chiếu chưa định nghĩa đến « %T »\n" + +#: ldmain.c:1381 +msgid "%B: warning: undefined reference to `%T'\n" +msgstr "%B: cảnh báo : chưa định nghĩa lời tham chiếu đến « %T »\n" + +#: ldmain.c:1387 +msgid "%X%B: more undefined references to `%T' follow\n" +msgstr "%X%B: có tham chiếu chưa định nghĩa đến « %T » thêm nữa theo sau\n" + +#: ldmain.c:1390 +msgid "%B: warning: more undefined references to `%T' follow\n" +msgstr "" +"%B: cảnh báo : chưa định nghĩa lời tham chiếu đến « %T » tại nhiều nơi nữa " +"theo đây\n" + +#: ldmain.c:1425 ldmain.c:1478 ldmain.c:1496 +msgid "%P%X: generated" +msgstr "%P%X: đã tạo ra" + +#: ldmain.c:1432 +msgid " additional relocation overflows omitted from the output\n" +msgstr "tràn định vị lại thêm bị bỏ đi khỏi dữ liệu xuất\n" + +#: ldmain.c:1445 +msgid " relocation truncated to fit: %s against undefined symbol `%T'" +msgstr "" +" sự định vị lại bị cắt xém để vừa: %s đối với ký hiệu chưa định nghĩa « %T »" + +#: ldmain.c:1450 +msgid "" +" relocation truncated to fit: %s against symbol `%T' defined in %A section " +"in %B" +msgstr "" +" sự định vị lại bị cắt xém để vừa: %s đối với ký hiệu « %T » đã định nghĩa " +"trong phần %A trong %B" + +#: ldmain.c:1460 +msgid " relocation truncated to fit: %s against `%T'" +msgstr "sự định vị lại bị cắt xém để vừa: %s đối với « %T »" + +#: ldmain.c:1481 +#, c-format +msgid "dangerous relocation: %s\n" +msgstr "sự định vị lại nguy hiểm: %s\n" + +#: ldmain.c:1499 +msgid " reloc refers to symbol `%T' which is not being output\n" +msgstr "" +" sự định vị lại tham chiếu đến ký hiệu « %T » mà không còn được xuất lại\n" + +#: ldmisc.c:149 +#, c-format +msgid "no symbol" +msgstr "không có ký hiệu" + +#: ldmisc.c:240 +#, c-format +msgid "built in linker script:%u" +msgstr "tập lệnh liên kết có sẵn:%u" + +#: ldmisc.c:289 ldmisc.c:293 +msgid "%B%F: could not read symbols\n" +msgstr "%B%F: không thể đọc các ký hiệu\n" + +#: ldmisc.c:329 +msgid "%B: In function `%T':\n" +msgstr "%B: trong hàm « %T »:\n" + +#: ldmisc.c:480 +msgid "%F%P: internal error %s %d\n" +msgstr "%F%P: lỗi nội bộ %s %d\n" + +#: ldmisc.c:526 +msgid "%P: internal error: aborting at %s line %d in %s\n" +msgstr "%P: lỗi nội bộ : đang hủy bỏ tại dòng %d trong %s\n" + +#: ldmisc.c:529 +msgid "%P: internal error: aborting at %s line %d\n" +msgstr "%P: lỗi nội bộ : đang hủy bỏ tại dòng %s trong %s\n" + +#: ldmisc.c:531 +msgid "%P%F: please report this bug\n" +msgstr "%P%F: vui lòng thông báo lỗi này\n" + +#. Output for noisy == 2 is intended to follow the GNU standards. +#: ldver.c:38 +#, c-format +msgid "GNU ld version %s\n" +msgstr "Trình ld phiên bản %s của GNU\n" + +#: ldver.c:52 +#, c-format +msgid " Supported emulations:\n" +msgstr " Mô phỏng đã hỗ trợ :\n" + +#: ldwrite.c:55 ldwrite.c:191 +msgid "%P%F: bfd_new_link_order failed\n" +msgstr "%P%F: việc « bfd_new_link_order » (bfd mới liên kết thứ tự) bị lỗi\n" + +#: ldwrite.c:341 +msgid "%F%P: cannot create split section name for %s\n" +msgstr "%F%P: không thể tạo tên phần đã chia tách cho %s\n" + +#: ldwrite.c:353 +msgid "%F%P: clone section failed: %E\n" +msgstr "%F%P: việc bắt chước phần bị lỗi: %E\n" + +#: ldwrite.c:391 +#, c-format +msgid "%8x something else\n" +msgstr "%8x cái gì khác\n" + +#: ldwrite.c:561 +msgid "%F%P: final link failed: %E\n" +msgstr "%F%P: liên kết cuối cùng bị lỗi: %E\n" + +#: lexsup.c:195 lexsup.c:327 +msgid "KEYWORD" +msgstr "TỪ_KHÓA" + +#: lexsup.c:195 +msgid "Shared library control for HP/UX compatibility" +msgstr "Điều khiển thư viên dùng chung để tương thích với HP/UX" + +#: lexsup.c:198 +msgid "ARCH" +msgstr "ARCH" + +#: lexsup.c:198 +msgid "Set architecture" +msgstr "Lập kiến trúc" + +#: lexsup.c:200 lexsup.c:421 +msgid "TARGET" +msgstr "ĐÍCH" + +#: lexsup.c:200 +msgid "Specify target for following input files" +msgstr "Ghi rõ đích cho những tập tin nhập theo đây" + +#: lexsup.c:203 +msgid "Read MRI format linker script" +msgstr "Đọc tập lệnh liên kết khuôn dạng MRI" + +#: lexsup.c:205 +msgid "Force common symbols to be defined" +msgstr "Ép buộc định nghĩa mọi ký hiệu dùng chung" + +#: lexsup.c:209 lexsup.c:475 lexsup.c:477 lexsup.c:479 +#: ../data/contact-lookup-applet.glade.h:5 +msgid "ADDRESS" +msgstr "ĐỊA CHỈ" + +#: lexsup.c:209 +msgid "Set start address" +msgstr "Lập địa chỉ bắt đầu" + +#: lexsup.c:211 +msgid "Export all dynamic symbols" +msgstr "Xuất mọi ký hiệu động" + +#: lexsup.c:213 +msgid "Link big-endian objects" +msgstr "Liên kết mọi đối tượng big-endian (cuối lớn)" + +#: lexsup.c:215 +msgid "Link little-endian objects" +msgstr "Liên kết mọi đối tượng little-endian (cuối nhỏ)" + +#: lexsup.c:217 lexsup.c:220 +msgid "SHLIB" +msgstr "SHLIB" + +#: lexsup.c:217 +msgid "Auxiliary filter for shared object symbol table" +msgstr "Bộ lọc phụ cho bảng ký hiệu đối tượng dùng chung" + +#: lexsup.c:220 +msgid "Filter for shared object symbol table" +msgstr "Bộ lọc cho bảng ký hiệu đối tượng dùng chung" + +#: lexsup.c:223 ../pan/filter-edit-ui.c:859 +msgid "Ignored" +msgstr "Bị bỏ qua" + +#: lexsup.c:225 ../gnotravex/gnotravex.c:245 +#: ../msearch/medusa-command-line-search.c:159 +msgid "SIZE" +msgstr "CỠ" + +#: lexsup.c:225 +msgid "Small data size (if no size, same as --shared)" +msgstr "Kích cỡ dữ liệu nhỏ (nếu không có, nó bằng tùy chọn « --shared »)" + +#: lexsup.c:228 ../gnome-stones/main.c:76 ../src/option.c:326 +#: ../src/option.c:600 +msgid "FILENAME" +msgstr "TÊN TẬP TIN" + +#: lexsup.c:228 +msgid "Set internal name of shared library" +msgstr "Lập tên nội bộ của thư viên dùng chung" + +#: lexsup.c:230 +msgid "PROGRAM" +msgstr "CHƯƠNG TRÌNH" + +#: lexsup.c:230 +msgid "Set PROGRAM as the dynamic linker to use" +msgstr "Lập CHƯƠNG TRÌNH là bộ liên kết động cần dùng" + +#: lexsup.c:233 +msgid "LIBNAME" +msgstr "TÊN THƯ VIÊN" + +#: lexsup.c:233 +msgid "Search for library LIBNAME" +msgstr "Tìm kiếm thư viên TÊN THƯ VIÊN" + +#: lexsup.c:235 src/fe-gtk/fe-gtk.c:172 ../utils/gpilotd-client.c:46 +#: ../activation-server/activation-server-main.c:84 +msgid "DIRECTORY" +msgstr "THƯ MỤC" + +#: lexsup.c:235 +msgid "Add DIRECTORY to library search path" +msgstr "Thêm THƯ MỤC vào đường dẫn tìm kiếm thư viên" + +#: lexsup.c:238 +msgid "Override the default sysroot location" +msgstr "Đè lên địa điểm sysroot (gốc hệ thống) mặc định" + +#: lexsup.c:240 +msgid "EMULATION" +msgstr "MÔ PHỎNG" + +#: lexsup.c:240 +msgid "Set emulation" +msgstr "Lập cách mô phỏng" + +#: lexsup.c:242 +msgid "Print map file on standard output" +msgstr "In tập tin bản đồ ra thiết bị xuất chuẩn" + +#: lexsup.c:244 +msgid "Do not page align data" +msgstr "Đừng canh lề trang dữ liệu" + +#: lexsup.c:246 +msgid "Do not page align data, do not make text readonly" +msgstr "Đừng canh lề trang dữ liệu, đừng lập văn bản là chỉ đọc" + +#: lexsup.c:249 +msgid "Page align data, make text readonly" +msgstr "Canh lề trang dữ liệu, lập văn bản là chỉ đọc" + +#: lexsup.c:252 +msgid "Set output file name" +msgstr "Lập tên tập tin xuất" + +#: lexsup.c:254 +msgid "Optimize output file" +msgstr "Ưu tiên hóa tập tin xuất" + +#: lexsup.c:256 +msgid "Ignored for SVR4 compatibility" +msgstr "Bị bỏ qua để tương thích với SVR4" + +#: lexsup.c:260 +msgid "Generate relocatable output" +msgstr "Tạo ra dữ liệu có thể định vị lại" + +#: lexsup.c:264 +msgid "Just link symbols (if directory, same as --rpath)" +msgstr "Chỉ liên kết ký hiệu (nếu thư mục, bằng tùy chọn « --rpath »)" + +#: lexsup.c:267 +msgid "Strip all symbols" +msgstr "Tước mọi ký hiệu" + +#: lexsup.c:269 +msgid "Strip debugging symbols" +msgstr "Tước ký hiệu gỡ lối" + +#: lexsup.c:271 +msgid "Strip symbols in discarded sections" +msgstr "Tước ký hiệu trong phần bị hủy" + +#: lexsup.c:273 +msgid "Do not strip symbols in discarded sections" +msgstr "Đừng tước ký hiệu trong phần bị hủy" + +#: lexsup.c:275 +msgid "Trace file opens" +msgstr "Tập tin vết có mở" + +#: lexsup.c:277 +msgid "Read linker script" +msgstr "Đọc tập lệnh liên kết" + +#: lexsup.c:279 lexsup.c:297 lexsup.c:363 lexsup.c:378 lexsup.c:468 +#: lexsup.c:493 lexsup.c:520 +msgid "SYMBOL" +msgstr "KÝ HIỆU" + +#: lexsup.c:279 +msgid "Start with undefined reference to SYMBOL" +msgstr "Bắt đầu với tham chiệu gạch chân đến KÝ HIỆU" + +#: lexsup.c:282 +msgid "[=SECTION]" +msgstr "[=PHẦN]" + +#: lexsup.c:283 +msgid "Don't merge input [SECTION | orphan] sections" +msgstr "Đừng kết hợp phần nhập [PHẦN | mồ côi]" + +#: lexsup.c:285 +msgid "Build global constructor/destructor tables" +msgstr "Xây dụng bảng cấu tạo/phá toàn cục" + +#: lexsup.c:287 schroot/schroot.c:73 schroot/schroot-options.cc:64 +#: schroot/schroot-releaselock-options.cc:48 +msgid "Print version information" +msgstr "In ra thông tin phiên bản" + +#: lexsup.c:289 +msgid "Print version and emulation information" +msgstr "In ra thông tin phiên bản và mô phỏng" + +#: lexsup.c:291 +msgid "Discard all local symbols" +msgstr "Hủy mọi ký hiệu cục bộ" + +#: lexsup.c:293 +msgid "Discard temporary local symbols (default)" +msgstr "Hủy mọi ký hiệu cục bộ tạm thời (mặc định)" + +#: lexsup.c:295 +msgid "Don't discard any local symbols" +msgstr "Đừng hủy ký hiệu cục bộ nào" + +#: lexsup.c:297 +msgid "Trace mentions of SYMBOL" +msgstr "Vết nơi ghi KÝ HIỆU" + +#: lexsup.c:299 +msgid "Default search path for Solaris compatibility" +msgstr "Đường dẫn tìm kiếm để tương thích với Solaris" + +#: lexsup.c:302 +msgid "Start a group" +msgstr "Bắt đầu nhóm" + +#: lexsup.c:304 +msgid "End a group" +msgstr "Kết thúc nhóm" + +#: lexsup.c:308 +msgid "Accept input files whose architecture cannot be determined" +msgstr "Chấp nhận tập tin nhập có kiến trức không thể được tháo gỡ" + +#: lexsup.c:312 +msgid "Reject input files whose architecture is unknown" +msgstr "Từ chối tập tin nhập có kiến trức lạ" + +#: lexsup.c:315 +msgid "" +"Set DT_NEEDED tags for DT_NEEDED entries in\n" +"\t\t\t\tfollowing dynamic libs" +msgstr "" +"Lập thẻ « DT_NEEDED » (cần thiết DT)\n" +"\tcho mục nhập « DT_NEEDED »\n" +"\ttrong những thư viên động theo đây" + +#: lexsup.c:318 +msgid "" +"Do not set DT_NEEDED tags for DT_NEEDED entries\n" +"\t\t\t\tin following dynamic libs" +msgstr "" +"Đừng lập thẻ « DT_NEEDED » (cần thiết DT)\n" +"\tcho mục nhập « DT_NEEDED »\n" +"\ttrong những thư viên động theo đây" + +#: lexsup.c:321 +msgid "Only set DT_NEEDED for following dynamic libs if used" +msgstr "" +"Chỉ lập thẻ « DT_NEEDED » (cần thiết DT)\n" +"\tcho những thư viên động theo đây nếu được dùng" + +#: lexsup.c:324 +msgid "Always set DT_NEEDED for following dynamic libs" +msgstr "" +"Luôn lập thẻ « DT_NEEDED » (cần thiết DT)\n" +"\tcho những thư viên động theo đây" + +#: lexsup.c:327 +msgid "Ignored for SunOS compatibility" +msgstr "Bị bỏ qua để tương thích với SunOS" + +#: lexsup.c:329 +msgid "Link against shared libraries" +msgstr "Liên kết đối với thư viên dùng chung" + +#: lexsup.c:335 +msgid "Do not link against shared libraries" +msgstr "Đừng liên kết đối với thư viên dùng chung" + +#: lexsup.c:343 +msgid "Bind global references locally" +msgstr "Đóng kết tham chiếu toàn cục một cách địa phương" + +#: lexsup.c:345 +msgid "Check section addresses for overlaps (default)" +msgstr "Kiểm tra địa chỉ phần có chồng chéo (mặc định)" + +msgid "Do not check section addresses for overlaps" +msgstr "Đừng kiểm tra địa chỉ phần có chồng chéo" + +#: lexsup.c:351 +msgid "Output cross reference table" +msgstr "Xuất bảng tham chiếu chéo" + +#: lexsup.c:353 +msgid "SYMBOL=EXPRESSION" +msgstr "KÝ HIỆU=BIỂU THỨC" + +#: lexsup.c:353 +msgid "Define a symbol" +msgstr "Định nghĩa ký hiệu" + +#: lexsup.c:355 +msgid "[=STYLE]" +msgstr "[=KIỂU DÁNG]" + +#: lexsup.c:355 +msgid "Demangle symbol names [using STYLE]" +msgstr "Tháo gỡ tên ký hiệu [bằng KIỂU DÁNG]" + +#: lexsup.c:358 +msgid "Generate embedded relocs" +msgstr "Tạo ra sự định vị lại nhúng" + +#: lexsup.c:360 +msgid "Treat warnings as errors" +msgstr "Xử lý cảnh báo là lỗi" + +#: lexsup.c:363 +msgid "Call SYMBOL at unload-time" +msgstr "Gọi KÝ HIỆU vào lúc bỏ tải" + +#: lexsup.c:365 +msgid "Force generation of file with .exe suffix" +msgstr "Ép buộc tạo ra tập tin có hậu tố « .exe »" + +#: lexsup.c:367 +msgid "Remove unused sections (on some targets)" +msgstr "Gỡ bỏ phần không dùng (trên một số đích)" + +#: lexsup.c:370 +msgid "Don't remove unused sections (default)" +msgstr "Đừng gỡ bỏ phần không dùng (mặc định)" + +#: lexsup.c:373 +msgid "Set default hash table size close to " +msgstr "Lập kích cỡ bảng băm mặc định là gần " + +#: lexsup.c:376 +msgid "Print option help" +msgstr "In ra trợ giúp về tùy chọn" + +#: lexsup.c:378 +msgid "Call SYMBOL at load-time" +msgstr "Gọi KÝ HIỆU vào lúc tải" + +#: lexsup.c:380 +msgid "Write a map file" +msgstr "Ghi tập tin bản đồ" + +#: lexsup.c:382 +msgid "Do not define Common storage" +msgstr "Đừng định nghĩa kho dùng chung" + +#: lexsup.c:384 +msgid "Do not demangle symbol names" +msgstr "Đừng tháo gỡ tên ký hiệu" + +#: lexsup.c:386 +msgid "Use less memory and more disk I/O" +msgstr "Chiếm ít bộ nhớ hơn, và nhiều nhập/xuất đĩa hơn" + +#: lexsup.c:388 +msgid "Do not allow unresolved references in object files" +msgstr "Đừng cho phép tham chiệu chưa tháo gỡ trong tập tin đối tượng" + +#: lexsup.c:391 +msgid "Allow unresolved references in shared libaries" +msgstr "Cho phép tham chiệu chưa tháo gỡ trong thư viên dùng chung" + +#: lexsup.c:395 +msgid "Do not allow unresolved references in shared libs" +msgstr "Đừng cho phép tham chiệu chưa tháo gỡ trong thư viên dùng chung" + +#: lexsup.c:399 +msgid "Allow multiple definitions" +msgstr "Cho phép nhiều lời định nghĩa" + +#: lexsup.c:401 +msgid "Disallow undefined version" +msgstr "Bỏ cho phép phiên bản chưa định nghĩa" + +#: lexsup.c:403 +msgid "Create default symbol version" +msgstr "Tạo phiên bản ký hiệu mặc định" + +#: lexsup.c:406 +msgid "Create default symbol version for imported symbols" +msgstr "Tạo phiên bản ký hiệu mặc định cho ký hiệu đã nhập" + +#: lexsup.c:409 +msgid "Don't warn about mismatched input files" +msgstr "Đừng cảnh báo về tập tin nhập không khớp với nhau" + +#: lexsup.c:411 +msgid "Turn off --whole-archive" +msgstr "Tắt tùy chọn « --whole-archive » (toàn kho)" + +#: lexsup.c:413 +msgid "Create an output file even if errors occur" +msgstr "Tạo tập tin xuất dù gặp lỗi" + +#: lexsup.c:418 +msgid "" +"Only use library directories specified on\n" +"\t\t\t\tthe command line" +msgstr "" +"Chỉ dùng thư mục thư viên\n" +"\tđược ghi rõ trên dòng lệnh" + +#: lexsup.c:421 +msgid "Specify target of output file" +msgstr "Ghi rõ đích của tập tin xuất" + +#: lexsup.c:424 +msgid "Ignored for Linux compatibility" +msgstr "Bị bỏ qua để tương thích với Linux" + +#: lexsup.c:427 +msgid "Reduce memory overheads, possibly taking much longer" +msgstr "Giảm bộ nhớ duy tu, có thể mất rất nhiều thời gian hơn" + +#: lexsup.c:430 +msgid "Relax branches on certain targets" +msgstr "Lơi ra nhánh trên một số đích nào đó" + +#: lexsup.c:433 +msgid "Keep only symbols listed in FILE" +msgstr "Giữ chỉ những ký hiệu được liệt kê trong TẬP TIN" + +#: lexsup.c:435 +msgid "Set runtime shared library search path" +msgstr "Lập đường dẫn tìm kiếm thư viên dùng chung vào lúc chạy" + +#: lexsup.c:437 +msgid "Set link time shared library search path" +msgstr "Lập đường dẫn tìm kiếm thư viên dùng chung vào lúc liên kết" + +#: lexsup.c:440 +msgid "Create a shared library" +msgstr "Tạo thư viên dùng chung" + +#: lexsup.c:444 +msgid "Create a position independent executable" +msgstr "Tạo ứng dụng chạy được không phụ thuộc vào vị trí" + +#: lexsup.c:448 +msgid "Sort common symbols by size" +msgstr "Sắp xếp ký hiệu dùng chung theo kích cỡ" + +#: lexsup.c:452 +msgid "name|alignment" +msgstr "tên|canh_hàng" + +#: lexsup.c:453 +msgid "Sort sections by name or maximum alignment" +msgstr "Sắp xếp phần theo tên hay canh lề tối đa" + +#: lexsup.c:455 +msgid "COUNT" +msgstr "SỐ_ĐẾM" + +#: lexsup.c:455 +msgid "How many tags to reserve in .dynamic section" +msgstr "Số thẻ cần giữ lại trong phần « .dynamic » (động)" + +#: lexsup.c:458 +msgid "[=SIZE]" +msgstr "[=CỠ]" + +#: lexsup.c:458 +msgid "Split output sections every SIZE octets" +msgstr "Chia tách phần xuất tại mỗi CỠ bộ tám" + +#: lexsup.c:461 +msgid "[=COUNT]" +msgstr "[=SỐ_ĐẾM]" + +#: lexsup.c:461 +msgid "Split output sections every COUNT relocs" +msgstr "Chia tách phần xuất tại mỗi SỐ_ĐẾM việc định vị lại" + +#: lexsup.c:464 +msgid "Print memory usage statistics" +msgstr "In ra thống kê cách sử dụng bộ nhớ" + +#: lexsup.c:466 +msgid "Display target specific options" +msgstr "Hiển thị tùy chọn đặc trưng cho đích" + +#: lexsup.c:468 +msgid "Do task level linking" +msgstr "Liên kết trong lớp tác vụ" + +#: lexsup.c:470 +msgid "Use same format as native linker" +msgstr "Dùng cùng khuôn dạng với bộ liên kết sở hữu" + +#: lexsup.c:472 +msgid "SECTION=ADDRESS" +msgstr "PHẦN=ĐỊA CHỈ" + +#: lexsup.c:472 +msgid "Set address of named section" +msgstr "Lập địa chỉ của phần có tên" + +#: lexsup.c:475 +msgid "Set address of .bss section" +msgstr "Lập địa chỉ của phần « .bss »" + +#: lexsup.c:477 +msgid "Set address of .data section" +msgstr "Lập địa chỉ của phần « .data » (dữ liệu)" + +#: lexsup.c:479 +msgid "Set address of .text section" +msgstr "Lập địa chỉ của phần « .text » (văn bản)" + +#: lexsup.c:482 +msgid "" +"How to handle unresolved symbols. is:\n" +"\t\t\t\tignore-all, report-all, ignore-in-object-files,\n" +"\t\t\t\tignore-in-shared-libs" +msgstr "" +"Cách quản lý ký hiệu chưa tháo gỡ.\n" +" \t là:\n" +" • ignore-all\t\t\t\tbỏ qua hết\n" +" • report-all\t\t\t\tthông báo hết\n" +" • ignore-in-object-files\tbỏ qua trong tập tin đối tượng\n" +" • ignore-in-shared-libs\tbỏ qua trong thư viên dùng chung" + +#: lexsup.c:486 +msgid "Output lots of information during link" +msgstr "Xuất nhiều thông tin trong khi liên kết" + +#: lexsup.c:490 +msgid "Read version information script" +msgstr "Đọc tập lệnh thông tin phiên bản" + +#: lexsup.c:493 +msgid "" +"Take export symbols list from .exports, using\n" +"\t\t\t\tSYMBOL as the version." +msgstr "" +"Lấy danh sách ký hiệu xuất từ « .exports » (xuất),\n" +"\t\tvới phiên bản là KÝ HIỆU" + +#: lexsup.c:496 +msgid "Warn about duplicate common symbols" +msgstr "Cảnh báo về ký hiệu dùng chung trùng" + +#: lexsup.c:498 +msgid "Warn if global constructors/destructors are seen" +msgstr "Cảnh báo nếu gặp bộ cấu tạo/phá toàn cục" + +#: lexsup.c:501 +msgid "Warn if the multiple GP values are used" +msgstr "Cảnh báo nếu sử dụng nhiều giá trị GP" + +#: lexsup.c:503 +msgid "Warn only once per undefined symbol" +msgstr "Cảnh báo chỉ một lần về mỗi ký hiệu chưa định nghĩa" + +#: lexsup.c:505 +msgid "Warn if start of section changes due to alignment" +msgstr "Cảnh báo nếu đầu phần thay đổi vì canh lề" + +#: lexsup.c:508 +msgid "Warn if shared object has DT_TEXTREL" +msgstr "Cảnh báo nếu đối tượng dùng chung có « DT_TEXTREL »" + +#: lexsup.c:512 +msgid "Report unresolved symbols as warnings" +msgstr "Thông báo ký hiệu chưa tháo gỡ là cảnh báo" + +#: lexsup.c:515 +msgid "Report unresolved symbols as errors" +msgstr "Thông báo ký hiệu chưa tháo gỡ là lỗi" + +#: lexsup.c:517 +msgid "Include all objects from following archives" +msgstr "Gồm mọi đối tượng từ những kho theo đây" + +#: lexsup.c:520 +msgid "Use wrapper functions for SYMBOL" +msgstr "Sử dụng hàm cuốn cho KÝ HIỆU" + +#: lexsup.c:667 +msgid "%P: unrecognized option '%s'\n" +msgstr "%P: không nhận ra tùy chọn « %s »\n" + +#: lexsup.c:669 +msgid "%P%F: use the --help option for usage information\n" +msgstr "" +"%P%F: hãy sử dụng tùy chọn « --help » để xem thông tin về cách sử dụng\n" + +#: lexsup.c:687 +msgid "%P%F: unrecognized -a option `%s'\n" +msgstr "%P%F: không nhận ra tùy chọn kiểu « -a » là « %s »\n" + +#: lexsup.c:700 +msgid "%P%F: unrecognized -assert option `%s'\n" +msgstr "%P%F: không nhận ra tùy chọn kiểu « -assert » (khẳng định) là « %s »\n" + +#: lexsup.c:743 +msgid "%F%P: unknown demangling style `%s'" +msgstr "%F%Ps: không biết kiểu dáng tháo gõ « %s »" + +#: lexsup.c:805 +msgid "%P%F: invalid number `%s'\n" +msgstr "%P%F: số không hợp lệ « %s »\n" + +#: lexsup.c:897 +msgid "%P%F: bad --unresolved-symbols option: %s\n" +msgstr "" +"%P%F: tùy chọn « --unresolved-symbols » (các ký hiệu chưa tháo gỡ) sai : %s\n" + +#: lexsup.c:968 +msgid "%P%F: bad -rpath option\n" +msgstr "%P%F: tùy chọn « -rpath » (đường dẫn r) sai\n" + +#: lexsup.c:1080 +msgid "%P%F: -shared not supported\n" +msgstr "%P%F: không hỗ trợ tùy chọn « -shared » (dùng chung)\n" + +#: lexsup.c:1089 +msgid "%P%F: -pie not supported\n" +msgstr "%P%F: không hỗ trợ tùy chọn « -pie » (bánh)\n" + +#: lexsup.c:1099 gphoto2/main.c:195 gphoto2/main.c:196 cg_print.c:98 +#: hist.c:385 ui/bookmarks.glade.h:49 plugins/dbus/xchat-remote.c:47 +msgid "name" +msgstr "tên" + +#: lexsup.c:1104 +msgid "%P%F: invalid section sorting option: %s\n" +msgstr "%P%F: tùy chọn sắp xếp phần không hợp lệ: %s\n" + +#: lexsup.c:1130 +msgid "%P%F: invalid argument to option \"--section-start\"\n" +msgstr "%P%F: đối số không hợp lệ đối với tùy chọn « --section-start »\n" + +#: lexsup.c:1137 +msgid "%P%F: missing argument(s) to option \"--section-start\"\n" +msgstr "" +"%P%F: thiếu đối số đối với tùy chọn « --section-start » (bắt đầu phần)\n" + +#: lexsup.c:1311 +msgid "%P%F: may not nest groups (--help for usage)\n" +msgstr "" +"%P%F: không cho phép lồng nhóm với nhau (« --help » để xem cách sử dụng " +"đúng)\n" + +#: lexsup.c:1318 +msgid "%P%F: group ended before it began (--help for usage)\n" +msgstr "" +"%P%F: nhóm kết thúc trước bắt đầu (« --help » để xem cách sử dụng đúng)\n" + +#: lexsup.c:1346 +msgid "%P%X: --hash-size needs a numeric argument\n" +msgstr "" +"%P%X: tùy chọn « --hash-size » (kích cỡ băm) cần thiết đối số thuộc số\n" + +#: lexsup.c:1397 lexsup.c:1410 +msgid "%P%F: invalid hex number `%s'\n" +msgstr "%P%F: số thập lục không hợp lệ « %s »\n" + +#: lexsup.c:1445 +#, c-format +msgid "Usage: %s [options] file...\n" +msgstr "Cách sử dụng: %s tập_tin...\n" + +#: lexsup.c:1447 main.c:292 +#, c-format +msgid "Options:\n" +msgstr "Tùy chọn:\n" + +#: lexsup.c:1538 +#, c-format +msgid "%s: supported emulations: " +msgstr "%s: mô phỏng hỗ trợ :" + +#: lexsup.c:1543 +#, c-format +msgid "%s: emulation specific options:\n" +msgstr "%s: tùy chọn đặc trưng cho mô phỏng:\n" + +#: mri.c:291 +msgid "%P%F: unknown format type %s\n" +msgstr "%P%F: không biết kiểu khuôn dạng %s\n" + +#: pe-dll.c:303 +#, c-format +msgid "%XUnsupported PEI architecture: %s\n" +msgstr "%XChưa hỗ trợ kiến trúc PEI: %s\n" + +#: pe-dll.c:652 +#, c-format +msgid "%XError, duplicate EXPORT with ordinals: %s (%d vs %d)\n" +msgstr "%XLỗi: XUẤT trùng với điều thứ tự : %s (%d so với %d)\n" + +#: pe-dll.c:659 +#, c-format +msgid "Warning, duplicate EXPORT: %s\n" +msgstr "Cảnh báo, XUẤT trùng: %s\n" + +#: pe-dll.c:725 +#, c-format +msgid "%XCannot export %s: symbol not defined\n" +msgstr "%XKhông thể xuất %s: chưa định nghĩa ký hiệu\n" + +#: pe-dll.c:731 +#, c-format +msgid "%XCannot export %s: symbol wrong type (%d vs %d)\n" +msgstr "%XKhông thể xuất %s: ký hiệu sai kiểu (%d so với %d)\n" + +#: pe-dll.c:738 +#, c-format +msgid "%XCannot export %s: symbol not found\n" +msgstr "%XKhông thể xuất %s: không tìm thấy ký hiệu\n" + +#: pe-dll.c:850 +#, c-format +msgid "%XError, ordinal used twice: %d (%s vs %s)\n" +msgstr "%XLỗi, điều thứ tự được dùng hai lần: %d (%s so với %s)\n" + +#: pe-dll.c:1172 +#, c-format +msgid "%XError: %d-bit reloc in dll\n" +msgstr "%xLỗi: định vị lại %d-bit trong DLL\n" + +#: pe-dll.c:1300 +#, c-format +msgid "%s: Can't open output def file %s\n" +msgstr "%s: Không thể mở tập tin xuất def (định nghĩa) %s\n" + +#: pe-dll.c:1443 +#, c-format +msgid "; no contents available\n" +msgstr "; không có nội dung sẵn sàng\n" + +#: pe-dll.c:2205 +msgid "" +"%C: variable '%T' can't be auto-imported. Please read the documentation for " +"ld's --enable-auto-import for details.\n" +msgstr "" +"%C: không thể tự động nhập biến « %T ». Hãy đọc tài liệu hướng dẫn về tùy " +"chọn « --enable-auto-import » (bật nhập tự động) của trình ld, để xem chi " +"tiết.\n" + +#: pe-dll.c:2235 +#, c-format +msgid "%XCan't open .lib file: %s\n" +msgstr "%XKhông thể mở tập tin « .lib » (thư viên): %s\n" + +#: pe-dll.c:2240 +#, c-format +msgid "Creating library file: %s\n" +msgstr "Đang tạo tập tin thư viên: %s\n" + +#: src/plugins/language/language-compiler.c:37 +#, c-format +msgid "Please provide a list of klp files as arguments.\n" +msgstr "Hãy cung cấp danh sách các tập tin kiểu « klp » dạng đối số.\n" + +#: src/plugins/printable/dictionary-builder.c:113 +#, c-format +msgid "Error opening file `%s': %s\n" +msgstr "Gặp lỗi khi mở tập tin « %s »: %s\n" + +#: src/plugins/printable/dictionary-builder.c:74 +#, c-format +msgid "" +"Error allocating: %s\n" +"." +msgstr "" +"Gặp lỗi khi cấp phát: %s\n" +"." + +#: src/plugins/printable/dictionary-builder.c:86 +#, c-format +msgid "Increase ALLOCSIZE (in %s).\n" +msgstr "Tăng lên ALLOCSIZE (kích cỡ cấp phát, theo %s).\n" + +#: src/plugins/rpm/rpmextractor.c:3048 +#, c-format +msgid "Source RPM %d.%d" +msgstr "RPM nguồn %d.%d" + +#: src/plugins/rpm/rpmextractor.c:3053 +#, c-format +msgid "Binary RPM %d.%d" +msgstr "RPM nhị phân %d.%d" + +#: src/plugins/printable/dictionary-builder.c:53 +#, c-format +msgid "" +"Please provide the name of the language you are building\n" +"a dictionary for. For example:\n" +msgstr "" +"Hãy cung cấp tên ngôn ngữ mà bạn đang xây dụng từ điển cho nó. Lấy thí dụ :\n" + +#: ../gnopi/cmdmapui.c:1588 +msgid "Commands" +msgstr "Lệnh" + +#: src/plugins/manextractor.c:147 src/plugins/manextractor.c:133 +msgid "System calls" +msgstr "Cuộc gọi hệ thống" + +#: src/plugins/manextractor.c:152 src/plugins/manextractor.c:138 +msgid "Library calls" +msgstr "Cuộc gọi thư viên" + +#: src/plugins/manextractor.c:157 src/plugins/manextractor.c:143 +msgid "Special files" +msgstr "Tập tin đặc biệt" + +#: src/plugins/manextractor.c:162 src/plugins/manextractor.c:148 +msgid "File formats and conventions" +msgstr "Khuôn dang tập tin và quy ước" + +#: ../data/toc.xml.in.h:7 ../src/red_searchbox.py:179 ../src/util.c:339 +msgid "Games" +msgstr "Trò chơi" + +#: src/plugins/manextractor.c:172 src/plugins/manextractor.c:158 +msgid "Conventions and miscellaneous" +msgstr "Quy ước và linh tinh" + +#: src/plugins/manextractor.c:177 src/plugins/manextractor.c:163 +msgid "System management commands" +msgstr "Lệnh quản lý hệ thống" + +#: src/plugins/manextractor.c:182 src/plugins/manextractor.c:168 +msgid "Kernel routines" +msgstr "Thao tác hạt nhân" + +#: src/plugins/wavextractor.c:113 src/plugins/mp3extractor.c:434 +#: src/plugins/wavextractor.c:114 src/plugins/mp3extractor.c:438 +msgid "mono" +msgstr "một nguồn" + +#: ../audio-properties-view/audio-properties-view.c:171 +msgid "stereo" +msgstr "âm lập thể" + +#: src/plugins/jpegextractor.c:178 +#, c-format +msgid "%ux%u dots per inch" +msgstr "%ux%u chấm trên mỗi insơ" + +#: src/plugins/jpegextractor.c:188 +#, c-format +msgid "%ux%u dots per cm" +msgstr "%ux%u chấm trên mỗi cm" + +#: src/plugins/jpegextractor.c:198 +#, c-format +msgid "%ux%u dots per inch?" +msgstr "%ux%u chấm trên mỗi insơ?" + +#: src/plugins/riffextractor.c:167 +#, c-format +msgid "codec: %s, %u fps, %u ms" +msgstr "codec: %s, %u khung/giây, %u miligiây" + +#: src/plugins/mp3extractor.c:49 ../cddb-slave2/cddb-track-editor.c:78 +msgid "Blues" +msgstr "Blu" + +#: src/plugins/mp3extractor.c:50 +msgid "Classic Rock" +msgstr "Rốc cổ điển" + +#: ../src/Database.cs:813 ../src/Database.cs:833 ../glom/glom.glade.h:79 +#: ../mimedir/mimedir-vcard-address.c:216 +#: ../mimedir/mimedir-vcard-address.c:217 +msgid "Country" +msgstr "Quốc gia" + +#: src/plugins/mp3extractor.c:52 ../cddb-slave2/cddb-track-editor.c:81 +msgid "Dance" +msgstr "Khiêu vũ" + +#: src/plugins/mp3extractor.c:53 ../cddb-slave2/cddb-track-editor.c:82 +msgid "Disco" +msgstr "Đít-xcô" + +#: src/plugins/mp3extractor.c:54 ../cddb-slave2/cddb-track-editor.c:83 +msgid "Funk" +msgstr "Sôi nổi" + +#: src/plugins/mp3extractor.c:55 ../cddb-slave2/cddb-track-editor.c:84 +msgid "Grunge" +msgstr "Vỡ mộng" + +#: src/plugins/mp3extractor.c:56 ../cddb-slave2/cddb-track-editor.c:85 +msgid "Hip-Hop" +msgstr "Hít-họt" + +#: src/plugins/mp3extractor.c:57 ../cddb-slave2/cddb-track-editor.c:86 +msgid "Jazz" +msgstr "Ja" + +#: src/plugins/mp3extractor.c:58 ../cddb-slave2/cddb-track-editor.c:87 +msgid "Metal" +msgstr "Kim" + +#: src/plugins/mp3extractor.c:59 ../cddb-slave2/cddb-track-editor.c:88 +msgid "New Age" +msgstr "Thời kỳ mới" + +#: src/plugins/mp3extractor.c:60 ../cddb-slave2/cddb-track-editor.c:89 +msgid "Oldies" +msgstr "Cũ" + +#: src/plugins/mp3extractor.c:62 ../cddb-slave2/cddb-track-editor.c:91 +msgid "Pop" +msgstr "Pốp" + +#: src/plugins/mp3extractor.c:63 ../cddb-slave2/cddb-track-editor.c:92 +msgid "R&B" +msgstr "Nhịp điệu và blu" + +#: src/plugins/mp3extractor.c:64 ../cddb-slave2/cddb-track-editor.c:93 +msgid "Rap" +msgstr "Rap" + +#: src/plugins/mp3extractor.c:65 ../cddb-slave2/cddb-track-editor.c:94 +msgid "Reggae" +msgstr "Re-gê" + +#: src/plugins/mp3extractor.c:66 ../cddb-slave2/cddb-track-editor.c:95 +msgid "Rock" +msgstr "Rốc" + +#: src/plugins/mp3extractor.c:67 ../cddb-slave2/cddb-track-editor.c:96 +msgid "Techno" +msgstr "Kỹ thuật" + +#: src/plugins/mp3extractor.c:68 ../cddb-slave2/cddb-track-editor.c:97 +msgid "Industrial" +msgstr "Công nghiệp" + +#: src/plugins/mp3extractor.c:69 +msgid "Alternative" +msgstr "Sự chọn khác" + +#: src/plugins/mp3extractor.c:70 ../cddb-slave2/cddb-track-editor.c:99 +msgid "Ska" +msgstr "Ska" + +#: src/plugins/mp3extractor.c:71 ../cddb-slave2/cddb-track-editor.c:100 +msgid "Death Metal" +msgstr "Kim chết" + +#: src/plugins/mp3extractor.c:72 ../cddb-slave2/cddb-track-editor.c:101 +msgid "Pranks" +msgstr "Trò chơi ác" + +#: src/plugins/mp3extractor.c:73 ../cddb-slave2/cddb-track-editor.c:102 +msgid "Soundtrack" +msgstr "Nhạc của phím" + +#: src/plugins/mp3extractor.c:74 ../cddb-slave2/cddb-track-editor.c:103 +msgid "Euro-Techno" +msgstr "Kỹ thuật Âu" + +#: src/plugins/mp3extractor.c:75 ../cddb-slave2/cddb-track-editor.c:104 +msgid "Ambient" +msgstr "Chung quanh" + +#: src/plugins/mp3extractor.c:76 ../cddb-slave2/cddb-track-editor.c:105 +msgid "Trip-Hop" +msgstr "Tợ-rít-Hot" + +#: src/plugins/mp3extractor.c:77 ../cddb-slave2/cddb-track-editor.c:106 +msgid "Vocal" +msgstr "Thanh nhạc" + +#: src/plugins/mp3extractor.c:78 ../cddb-slave2/cddb-track-editor.c:107 +msgid "Jazz+Funk" +msgstr "Ja và Sôi nổi" + +#: src/plugins/mp3extractor.c:79 ../cddb-slave2/cddb-track-editor.c:108 +msgid "Fusion" +msgstr "Nóng chảy" + +#: src/plugins/mp3extractor.c:80 ../cddb-slave2/cddb-track-editor.c:109 +msgid "Trance" +msgstr "Hôn mê" + +#: src/plugins/mp3extractor.c:81 ../cddb-slave2/cddb-track-editor.c:110 +msgid "Classical" +msgstr "Cổ điển" + +#: src/plugins/mp3extractor.c:82 ../cddb-slave2/cddb-track-editor.c:111 +msgid "Instrumental" +msgstr "Bằng nhạc khí" + +#: src/plugins/mp3extractor.c:83 ../cddb-slave2/cddb-track-editor.c:112 +msgid "Acid" +msgstr "Axit" + +#: src/plugins/mp3extractor.c:84 ../sheets/ciscomisc.sheet.in.h:16 +#: ../cddb-slave2/cddb-track-editor.c:113 +msgid "House" +msgstr "Nhà" + +#: src/plugins/mp3extractor.c:85 ../src/ui/keyboard-properties.c:124 +msgid "Game" +msgstr "Trò chơi" + +#: src/plugins/mp3extractor.c:86 ../cddb-slave2/cddb-track-editor.c:115 +msgid "Sound Clip" +msgstr "Trích đoạn âm thanh" + +#: src/plugins/mp3extractor.c:87 ../cddb-slave2/cddb-track-editor.c:116 +msgid "Gospel" +msgstr "Phúc âm" + +#: src/plugins/mp3extractor.c:88 ../cddb-slave2/cddb-track-editor.c:117 +#: ../plug-ins/common/spheredesigner.c:296 +msgid "Noise" +msgstr "Ồn" + +#: src/plugins/mp3extractor.c:89 +msgid "Alt. Rock" +msgstr "Rốc thay thế" + +#: src/plugins/mp3extractor.c:90 sys/oss/gstossmixer.c:100 +#: ../cddb-slave2/cddb-track-editor.c:119 ext/alsa/gstalsamixertrack.c:84 +msgid "Bass" +msgstr "Trầm" + +#: src/plugins/mp3extractor.c:91 ../cddb-slave2/cddb-track-editor.c:120 +msgid "Soul" +msgstr "Hồn" + +#: src/plugins/mp3extractor.c:92 ../cddb-slave2/cddb-track-editor.c:121 +msgid "Punk" +msgstr "Rốc dữ dội" + +#: src/plugins/mp3extractor.c:93 ../src/util.c:361 +msgid "Space" +msgstr "Khoảng" + +#: src/plugins/mp3extractor.c:94 ../cddb-slave2/cddb-track-editor.c:123 +msgid "Meditative" +msgstr "Tĩnh tọa" + +#: src/plugins/mp3extractor.c:95 ../cddb-slave2/cddb-track-editor.c:124 +msgid "Instrumental Pop" +msgstr "Pốp bằng nhac khí" + +#: src/plugins/mp3extractor.c:96 ../cddb-slave2/cddb-track-editor.c:125 +msgid "Instrumental Rock" +msgstr "Rốc bằng nhạc khí" + +#: src/plugins/mp3extractor.c:97 ../cddb-slave2/cddb-track-editor.c:126 +msgid "Ethnic" +msgstr "Dân tộc" + +#: src/plugins/mp3extractor.c:98 ../cddb-slave2/cddb-track-editor.c:127 +msgid "Gothic" +msgstr "Gô-tích" + +#: src/plugins/mp3extractor.c:99 ../cddb-slave2/cddb-track-editor.c:128 +msgid "Darkwave" +msgstr "Sóng bóng" + +#: src/plugins/mp3extractor.c:100 ../cddb-slave2/cddb-track-editor.c:129 +msgid "Techno-Industrial" +msgstr "Kỹ thuật - Công nghiệp" + +#: src/plugins/mp3extractor.c:101 ../cddb-slave2/cddb-track-editor.c:130 +msgid "Electronic" +msgstr "Điện" + +#: src/plugins/mp3extractor.c:102 ../cddb-slave2/cddb-track-editor.c:131 +msgid "Pop-Folk" +msgstr "Pốp - Dân ca" + +#: src/plugins/mp3extractor.c:103 ../cddb-slave2/cddb-track-editor.c:132 +msgid "Eurodance" +msgstr "Khiêu vũ Âu" + +#: src/plugins/mp3extractor.c:104 ../cddb-slave2/cddb-track-editor.c:133 +msgid "Dream" +msgstr "Mơ mộng " + +#: src/plugins/mp3extractor.c:105 ../cddb-slave2/cddb-track-editor.c:134 +msgid "Southern Rock" +msgstr "Rốc Nam" + +#: src/plugins/mp3extractor.c:106 ../cddb-slave2/cddb-track-editor.c:135 +msgid "Comedy" +msgstr "Kịch vui" + +#: src/plugins/mp3extractor.c:107 ../cddb-slave2/cddb-track-editor.c:136 +msgid "Cult" +msgstr "Giáo phái" + +#: src/plugins/mp3extractor.c:108 ../cddb-slave2/cddb-track-editor.c:137 +msgid "Gangsta Rap" +msgstr "Rap Kẻ cướp" + +#: src/plugins/mp3extractor.c:109 ../cddb-slave2/cddb-track-editor.c:138 +msgid "Top 40" +msgstr "40 tốt nhất" + +#: src/plugins/mp3extractor.c:110 ../cddb-slave2/cddb-track-editor.c:139 +msgid "Christian Rap" +msgstr "Ráp Cơ-đốc" + +#: src/plugins/mp3extractor.c:111 ../cddb-slave2/cddb-track-editor.c:140 +msgid "Pop/Funk" +msgstr "Pốp/Sôi nổi" + +#: src/plugins/mp3extractor.c:112 ../cddb-slave2/cddb-track-editor.c:141 +msgid "Jungle" +msgstr "Rừng" + +#: src/plugins/mp3extractor.c:113 ../cddb-slave2/cddb-track-editor.c:142 +msgid "Native American" +msgstr "Mỹ bản xứ" + +#: src/plugins/mp3extractor.c:114 ../cddb-slave2/cddb-track-editor.c:143 +msgid "Cabaret" +msgstr "Ca-ba-rê" + +#: src/plugins/mp3extractor.c:115 ../cddb-slave2/cddb-track-editor.c:144 +msgid "New Wave" +msgstr "Sóng mới" + +#: src/plugins/mp3extractor.c:116 ../cddb-slave2/cddb-track-editor.c:145 +msgid "Psychedelic" +msgstr "Tạo ảo giác" + +#: src/plugins/mp3extractor.c:117 ../cddb-slave2/cddb-track-editor.c:146 +msgid "Rave" +msgstr "Rít" + +#: src/plugins/mp3extractor.c:118 ../cddb-slave2/cddb-track-editor.c:147 +msgid "Showtunes" +msgstr "Điệu kịch" + +#: src/plugins/mp3extractor.c:119 ../cddb-slave2/cddb-track-editor.c:148 +msgid "Trailer" +msgstr "Quảng cáo trước phím" + +#: src/plugins/mp3extractor.c:120 ../cddb-slave2/cddb-track-editor.c:149 +msgid "Lo-Fi" +msgstr "Độ trung thực thấp" + +#: src/plugins/mp3extractor.c:121 ../cddb-slave2/cddb-track-editor.c:150 +msgid "Tribal" +msgstr "Bộ lạc" + +#: src/plugins/mp3extractor.c:122 ../cddb-slave2/cddb-track-editor.c:151 +msgid "Acid Punk" +msgstr "Rốc dữ dội axit" + +#: src/plugins/mp3extractor.c:123 ../cddb-slave2/cddb-track-editor.c:152 +msgid "Acid Jazz" +msgstr "Ja axit" + +#: src/plugins/mp3extractor.c:124 ../cddb-slave2/cddb-track-editor.c:153 +msgid "Polka" +msgstr "Pôn-ca" + +#: src/plugins/mp3extractor.c:125 ../cddb-slave2/cddb-track-editor.c:154 +msgid "Retro" +msgstr "Lại sau" + +#: src/plugins/mp3extractor.c:126 ../cddb-slave2/cddb-track-editor.c:155 +msgid "Musical" +msgstr "Kịch nhạc" + +#: src/plugins/mp3extractor.c:127 ../cddb-slave2/cddb-track-editor.c:156 +msgid "Rock & Roll" +msgstr "Rốc en rôn" + +#: src/plugins/mp3extractor.c:128 ../cddb-slave2/cddb-track-editor.c:157 +msgid "Hard Rock" +msgstr "Rốc cứng" + +#: src/plugins/mp3extractor.c:129 ../cddb-slave2/cddb-track-editor.c:158 +msgid "Folk" +msgstr "Dân ca" + +#: src/plugins/mp3extractor.c:130 ../cddb-slave2/cddb-track-editor.c:159 +msgid "Folk/Rock" +msgstr "Dân ca/Rốc" + +#: src/plugins/mp3extractor.c:131 ../cddb-slave2/cddb-track-editor.c:160 +msgid "National Folk" +msgstr "Dân ca quốc gia" + +#: src/plugins/mp3extractor.c:132 ../cddb-slave2/cddb-track-editor.c:161 +msgid "Swing" +msgstr "Xuynh" + +#: src/plugins/mp3extractor.c:133 ../cddb-slave2/cddb-track-editor.c:162 +msgid "Fast-Fusion" +msgstr "Nóng chạy nhanh" + +#: src/plugins/mp3extractor.c:134 +msgid "Bebob" +msgstr "Bí-bọt" + +#: src/plugins/mp3extractor.c:135 +msgid "Latin" +msgstr "Dân tộc Tây-ban-nha" + +#: src/plugins/mp3extractor.c:136 ../cddb-slave2/cddb-track-editor.c:165 +msgid "Revival" +msgstr "Phục âm nhấn mạnh" + +#: src/plugins/mp3extractor.c:137 ../gedit/gedit-encodings.c:174 +#: ../cddb-slave2/cddb-track-editor.c:166 ../src/encoding.c:82 +msgid "Celtic" +msgstr "Xen-tơ" + +#: src/plugins/mp3extractor.c:138 ../cddb-slave2/cddb-track-editor.c:167 +msgid "Bluegrass" +msgstr "Cỏ xanh" + +#: src/plugins/mp3extractor.c:139 ../cddb-slave2/cddb-track-editor.c:168 +msgid "Avantgarde" +msgstr "Đi tiên phong" + +#: src/plugins/mp3extractor.c:140 ../cddb-slave2/cddb-track-editor.c:169 +msgid "Gothic Rock" +msgstr "Rốc Gô-tích" + +#: src/plugins/mp3extractor.c:141 ../cddb-slave2/cddb-track-editor.c:170 +msgid "Progressive Rock" +msgstr "Rốc tiến lên" + +#: src/plugins/mp3extractor.c:142 ../cddb-slave2/cddb-track-editor.c:171 +msgid "Psychedelic Rock" +msgstr "Rốc tạo ảo giác" + +#: src/plugins/mp3extractor.c:143 ../cddb-slave2/cddb-track-editor.c:172 +msgid "Symphonic Rock" +msgstr "Rốc giao hưởng" + +#: src/plugins/mp3extractor.c:144 ../cddb-slave2/cddb-track-editor.c:173 +msgid "Slow Rock" +msgstr "Rốc chậm" + +#: src/plugins/mp3extractor.c:145 ../cddb-slave2/cddb-track-editor.c:174 +msgid "Big Band" +msgstr "Dàn nhạc To" + +#: src/plugins/mp3extractor.c:146 ../cddb-slave2/cddb-track-editor.c:175 +msgid "Chorus" +msgstr "Hợp xướng" + +#: src/plugins/mp3extractor.c:147 ../cddb-slave2/cddb-track-editor.c:176 +msgid "Easy Listening" +msgstr "Nghe dễ dàng" + +#: src/plugins/mp3extractor.c:148 ../cddb-slave2/cddb-track-editor.c:177 +msgid "Acoustic" +msgstr "Độ trung thực âm thanh" + +#: src/plugins/mp3extractor.c:149 ../cddb-slave2/cddb-track-editor.c:178 +msgid "Humour" +msgstr "Hài hước" + +#: src/plugins/mp3extractor.c:150 +msgid "Speech" +msgstr "Nói tiếng" + +#: src/plugins/mp3extractor.c:151 ../cddb-slave2/cddb-track-editor.c:180 +msgid "Chanson" +msgstr "Bài hát kiểu Pháp" + +#: src/plugins/mp3extractor.c:152 ../cddb-slave2/cddb-track-editor.c:181 +msgid "Opera" +msgstr "Hát kịch" + +#: src/plugins/mp3extractor.c:153 ../cddb-slave2/cddb-track-editor.c:182 +msgid "Chamber Music" +msgstr "Nhạc phòng" + +#: src/plugins/mp3extractor.c:154 ../cddb-slave2/cddb-track-editor.c:183 +msgid "Sonata" +msgstr "Bản xô-nat" + +#: src/plugins/mp3extractor.c:155 ../cddb-slave2/cddb-track-editor.c:184 +msgid "Symphony" +msgstr "Giao hưởng" + +#: src/plugins/mp3extractor.c:156 ../cddb-slave2/cddb-track-editor.c:185 +msgid "Booty Bass" +msgstr "Trầm Booty" + +#: src/plugins/mp3extractor.c:157 ../cddb-slave2/cddb-track-editor.c:186 +msgid "Primus" +msgstr "Pri-mus" + +#: src/plugins/mp3extractor.c:158 ../cddb-slave2/cddb-track-editor.c:187 +msgid "Porn Groove" +msgstr "Porn Groove" + +#: src/plugins/mp3extractor.c:159 ../cddb-slave2/cddb-track-editor.c:188 +msgid "Satire" +msgstr "Châm biếm" + +#: src/plugins/mp3extractor.c:160 ../cddb-slave2/cddb-track-editor.c:189 +msgid "Slow Jam" +msgstr "Ứng tác chậm" + +#: src/plugins/mp3extractor.c:161 ../cddb-slave2/cddb-track-editor.c:190 +msgid "Club" +msgstr "Hội" + +#: src/plugins/mp3extractor.c:162 ../cddb-slave2/cddb-track-editor.c:191 +msgid "Tango" +msgstr "Tan-gô" + +#: src/plugins/mp3extractor.c:163 ../cddb-slave2/cddb-track-editor.c:192 +msgid "Samba" +msgstr "Sam-ba" + +#: src/plugins/mp3extractor.c:164 ../cddb-slave2/cddb-track-editor.c:193 +msgid "Folklore" +msgstr "Truyền thống dân gian" + +#: src/plugins/mp3extractor.c:165 ../cddb-slave2/cddb-track-editor.c:194 +msgid "Ballad" +msgstr "Khúc balat" + +#: src/plugins/mp3extractor.c:166 ../cddb-slave2/cddb-track-editor.c:195 +msgid "Power Ballad" +msgstr "Khúc balat năng lực" + +#: src/plugins/mp3extractor.c:167 ../cddb-slave2/cddb-track-editor.c:196 +msgid "Rhythmic Soul" +msgstr "Hồn nhịp nhàng" + +#: src/plugins/mp3extractor.c:168 ../cddb-slave2/cddb-track-editor.c:197 +msgid "Freestyle" +msgstr "Kiểu tự do" + +#: src/plugins/mp3extractor.c:169 ../cddb-slave2/cddb-track-editor.c:198 +msgid "Duet" +msgstr "Bản nhạc cho bộ đôi" + +#: src/plugins/mp3extractor.c:170 ../cddb-slave2/cddb-track-editor.c:199 +msgid "Punk Rock" +msgstr "Rốc - rốc dữ dội" + +#: src/plugins/mp3extractor.c:171 ../cddb-slave2/cddb-track-editor.c:200 +msgid "Drum Solo" +msgstr "Trống diễn đơn" + +#: src/plugins/mp3extractor.c:172 ../cddb-slave2/cddb-track-editor.c:201 +msgid "A Cappella" +msgstr "Hát không có nhạc hỗ trợ" + +#: src/plugins/mp3extractor.c:173 ../cddb-slave2/cddb-track-editor.c:202 +msgid "Euro-House" +msgstr "Nhà Âu" + +#: src/plugins/mp3extractor.c:174 ../cddb-slave2/cddb-track-editor.c:203 +msgid "Dance Hall" +msgstr "Phòng khiêu vũ" + +#: src/plugins/mp3extractor.c:175 ../cddb-slave2/cddb-track-editor.c:204 +msgid "Goa" +msgstr "Goa" + +#: src/plugins/mp3extractor.c:176 ../cddb-slave2/cddb-track-editor.c:205 +msgid "Drum & Bass" +msgstr "Trống và Trầm" + +#: src/plugins/mp3extractor.c:177 ../cddb-slave2/cddb-track-editor.c:206 +msgid "Club-House" +msgstr "Nhà hội" + +#: src/plugins/mp3extractor.c:178 ../cddb-slave2/cddb-track-editor.c:207 +msgid "Hardcore" +msgstr "Lõi cứng" + +#: src/plugins/mp3extractor.c:179 ../cddb-slave2/cddb-track-editor.c:208 +msgid "Terror" +msgstr "Kinh hãi" + +#: src/plugins/mp3extractor.c:180 ../cddb-slave2/cddb-track-editor.c:209 +msgid "Indie" +msgstr "In-đi" + +#: src/plugins/mp3extractor.c:181 ../cddb-slave2/cddb-track-editor.c:210 +msgid "BritPop" +msgstr "Pốp quốc Anh" + +#: src/plugins/mp3extractor.c:182 ../cddb-slave2/cddb-track-editor.c:211 +msgid "Negerpunk" +msgstr "Rốc dữ dội đen" + +#: src/plugins/mp3extractor.c:183 ../cddb-slave2/cddb-track-editor.c:212 +msgid "Polsk Punk" +msgstr "Rốc dữ dội Ba-lan" + +#: src/plugins/mp3extractor.c:184 +msgid "Beat" +msgstr "Nhịp phách" + +#: src/plugins/mp3extractor.c:185 ../cddb-slave2/cddb-track-editor.c:214 +msgid "Christian Gangsta Rap" +msgstr "Rap kẻ cướp Cơ đốc" + +#: src/plugins/mp3extractor.c:186 ../cddb-slave2/cddb-track-editor.c:215 +msgid "Heavy Metal" +msgstr "Kim nặng" + +#: src/plugins/mp3extractor.c:187 ../cddb-slave2/cddb-track-editor.c:216 +msgid "Black Metal" +msgstr "Kim đen" + +#: src/plugins/mp3extractor.c:188 ../cddb-slave2/cddb-track-editor.c:217 +msgid "Crossover" +msgstr "Xuyên chéo" + +#: src/plugins/mp3extractor.c:189 ../cddb-slave2/cddb-track-editor.c:218 +msgid "Contemporary Christian" +msgstr "Cơ-đốc đương thời" + +#: src/plugins/mp3extractor.c:190 ../cddb-slave2/cddb-track-editor.c:219 +msgid "Christian Rock" +msgstr "Rốc Cơ-đốc" + +#: src/plugins/mp3extractor.c:191 ../cddb-slave2/cddb-track-editor.c:220 +msgid "Merengue" +msgstr "Me-ren-gê" + +#: src/plugins/mp3extractor.c:192 ../cddb-slave2/cddb-track-editor.c:221 +msgid "Salsa" +msgstr "San-sa" + +#: src/plugins/mp3extractor.c:193 ../cddb-slave2/cddb-track-editor.c:222 +msgid "Thrash Metal" +msgstr "Kim quẫy đập" + +#: src/plugins/mp3extractor.c:194 ../cddb-slave2/cddb-track-editor.c:223 +msgid "Anime" +msgstr "A-ni-mê" + +#: src/plugins/mp3extractor.c:195 ../cddb-slave2/cddb-track-editor.c:224 +msgid "JPop" +msgstr "JPốp" + +#: src/plugins/mp3extractor.c:196 ../cddb-slave2/cddb-track-editor.c:225 +msgid "Synthpop" +msgstr "Pốp tổng hợp" + +#: src/plugins/mp3extractor.c:435 src/plugins/mp3extractor.c:439 +msgid "(variable bps)" +msgstr "(bit/giây thay đổi)" + +#: src/main/extract.c:49 src/doodle/help.c:51 +#, c-format +msgid "" +"Usage: %s\n" +"%s\n" +"\n" +msgstr "" +"Cách sử dụng: %s\n" +"%s\n" +"\n" + +#: src/main/extract.c:52 src/doodle/help.c:54 +#, c-format +msgid "" +"Arguments mandatory for long options are also mandatory for short options.\n" +msgstr "" +"Mọi đối số bắt buộc phải sử dụng với tùy chọn dài cũng bắt buộc với tùy chọn " +"ngắn.\n" + +#: src/main/extract.c:126 +msgid "do not remove any duplicates" +msgstr "đừng gỡ bỏ bản sao nào" + +#: src/main/extract.c:128 +msgid "print output in bibtex format" +msgstr "hiển thị dữ liệu xuất có dạng bibtex" + +#: src/main/extract.c:130 src/doodle/doodled.c:60 +msgid "" +"use the generic plaintext extractor for the language with the 2-letter " +"language code LANG" +msgstr "" +"sử dụng trình rút văn bản thuần thuộc giống loại cho ngôn ngữ có mã ngôn ngữ " +"bằng hai chữ là LANG" + +#: src/main/extract.c:132 +msgid "remove duplicates only if types match" +msgstr "gỡ bỏ bản sao chỉ nếu kiểu khớp thôi" + +#: src/main/extract.c:134 +msgid "use the filename as a keyword (loads filename-extractor plugin)" +msgstr "" +"dùng tên tập tin là một từ khoá (thì tải bộ cầm phít « filename-extractor " +"» [rút tên tập tin])" + +#: src/main/extract.c:136 +msgid "print this help" +msgstr "hiển thị trợ giúp này" + +#: src/main/extract.c:138 src/doodle/doodle.c:81 +msgid "compute hash using the given ALGORITHM (currently sha1 or md5)" +msgstr "tính băm bằng THUẬT TOÁN đã cho (hiện là sha1 hay md5)" + +#: src/main/extract.c:140 src/doodle/doodle.c:85 src/doodle/doodled.c:73 +msgid "load an extractor plugin named LIBRARY" +msgstr "tải một trình cầm phít rút có tên LIBRARY (THƯ VIÊN)" + +#: src/main/extract.c:142 +msgid "list all keyword types" +msgstr "liệt kê mọi kiểu từ khoá" + +#: src/main/extract.c:144 +msgid "do not use the default set of extractor plugins" +msgstr "đừng dùng bộ trình rút mặc định" + +#: src/main/extract.c:146 +msgid "print only keywords of the given TYPE (use -L to get a list)" +msgstr "" +"hiển thị chỉ từ khoá KIỂU (TYPE) đã cho thôi (dùng « -L » để xem danh sách)" + +#: src/main/extract.c:148 +msgid "remove duplicates even if keyword types do not match" +msgstr "gỡ bỏ bản sao thậm chí nếu kiểu từ khoá không khớp" + +#: src/main/extract.c:150 +msgid "use keyword splitting (loads split-extractor plugin)" +msgstr "" +"dùng khả năng xẻ từ khoá (thì tải bộ cầm phít « split-extractor » [rút xẻ])" + +#: src/main/extract.c:152 src/doodle/doodle.c:97 src/doodle/doodled.c:83 +msgid "print the version number" +msgstr "hiển thị số thứ tự phiên bản" + +#: src/main/extract.c:154 src/doodle/doodle.c:99 src/doodle/doodled.c:85 +msgid "be verbose" +msgstr "xuất chi tiết" + +#: src/main/extract.c:156 +msgid "do not print keywords of the given TYPE" +msgstr "đừng hiển thị từ khoá KIỂU (TYPE) đã cho" + +#: src/main/extract.c:159 +msgid "extract [OPTIONS] [FILENAME]*" +msgstr "" +"extract [TÙY_CHỌN] [TÊN_TẬP_TIN]*\n" +"[extract: rút]" + +#: src/main/extract.c:160 +msgid "Extract metadata from files." +msgstr "Rút siêu dữ liệu ra tập tin." + +#: src/main/extract.c:198 src/main/extractor.c:1121 src/main/extractor.c:784 +#, c-format +msgid "%s - (binary)\n" +msgstr "%s - (nhị phân)\n" + +#: src/main/extract.c:204 src/main/extractor.c:1126 src/main/extractor.c:789 +#, c-format +msgid "INVALID TYPE - %s\n" +msgstr "KIỂU KHÔNG HỢP LỆ — %s\n" + +#: src/main/extract.c:270 src/main/extractor.c:47 gst/gsttag.c:83 +#: src/main/extractor.c:40 +msgid "title" +msgstr "tựa" + +#: src/main/extract.c:272 src/main/extractor.c:45 gphoto2/main.c:1662 +#: src/main/extractor.c:38 +msgid "filename" +msgstr "tên tập tin" + +#: src/main/extract.c:277 src/main/extractor.c:48 src/main/extractor.c:41 +msgid "author" +msgstr "tác giả" + +#: src/main/extract.c:283 src/main/extractor.c:62 src/main/extractor.c:55 +msgid "keywords" +msgstr "từ khoá" + +#: src/main/extract.c:285 src/main/extractor.c:51 gst/gsttag.c:102 +#: src/main/extractor.c:44 +msgid "comment" +msgstr "chú thích" + +#: src/main/extract.c:289 src/main/extractor.c:52 gst/gsttag.c:94 +#: src/main/extractor.c:45 +msgid "date" +msgstr "ngày" + +#: src/main/extract.c:291 src/main/extractor.c:74 src/main/extractor.c:67 +msgid "creation date" +msgstr "ngày tạo" + +#: src/main/extract.c:319 src/main/extractor.c:53 src/main/extractor.c:46 +msgid "publisher" +msgstr "nhà xuất bản" + +#: src/main/extract.c:323 src/main/extractor.c:59 gst/gsttag.c:140 +#: src/main/extractor.c:52 +msgid "organization" +msgstr "tổ chức" + +#: src/main/extract.c:327 src/main/extractor.c:61 src/main/extractor.c:54 +msgid "subject" +msgstr "chủ đề" + +#: src/main/extract.c:331 src/main/extractor.c:78 src/main/extractor.c:71 +msgid "page count" +msgstr "tổng số trang" + +#: src/main/extract.c:474 +#, c-format +msgid "You must specify an argument for the `%s' option (option ignored).\n" +msgstr "Bạn phải ghi rõ một đối số cho tùy chọn « %s » (tùy chọn bị bỏ qua).\n" + +#: src/main/extract.c:541 src/main/extract.c:532 +#, c-format +msgid "Use --help to get a list of options.\n" +msgstr "" +"Hãy sử dụng lệnh « --help » (trợ giúp) để xem một danh sách các tùy chọn.\n" + +#: src/main/extract.c:600 src/main/extract.c:585 +#, c-format +msgid "%% BiBTeX file\n" +msgstr "%% tập tin BiBTeX\n" + +#: src/main/extract.c:617 src/main/extract.c:592 +#, c-format +msgid "Keywords for file %s:\n" +msgstr "Từ khoá cho tập tin %s:\n" + +#: src/main/extractor.c:46 src/main/extractor.c:39 +msgid "mimetype" +msgstr "kiểu MIME" + +#: src/main/extractor.c:49 gst/gsttag.c:86 src/main/extractor.c:42 +msgid "artist" +msgstr "nhạc sĩ" + +#: src/main/extractor.c:54 src/main/extractor.c:47 +msgid "language" +msgstr "ngôn ngữ" + +#: src/main/extractor.c:55 gst/gsttag.c:91 src/main/extractor.c:48 +msgid "album" +msgstr "tập" + +#: src/main/extractor.c:56 gst/gsttag.c:98 src/main/extractor.c:49 +msgid "genre" +msgstr "thể loại" + +#: ../providers/evolution/gda-calendar-model.c:40 +msgid "location" +msgstr "địa điểm" + +#: src/main/extractor.c:58 gst/gsttag.c:133 src/init.c:120 +#: src/main/extractor.c:51 +msgid "version" +msgstr "phiên bản" + +#: src/main/extractor.c:60 gst/gsttag.c:143 src/main/extractor.c:53 +msgid "copyright" +msgstr "bản quyền" + +#: src/main/extractor.c:63 src/main/extractor.c:56 +msgid "contributor" +msgstr "người đóng góp" + +#: src/main/extractor.c:64 src/main/extractor.c:57 +msgid "resource-type" +msgstr "kiểu tài nguyên" + +#: ../partman-basicmethods.templates:42 +msgid "format" +msgstr "định dạng" + +#: src/main/extractor.c:66 src/main/extractor.c:59 +msgid "resource-identifier" +msgstr "điều nhận diện tài nguyên" + +#: src/main/extractor.c:67 src/main/extractor.c:60 +msgid "source" +msgstr "nguồn" + +#: src/main/extractor.c:68 src/main/extractor.c:61 +msgid "relation" +msgstr "liên quan" + +#: src/main/extractor.c:69 src/main/extractor.c:62 +msgid "coverage" +msgstr "phạm vị" + +#: src/main/extractor.c:70 src/main/extractor.c:63 +msgid "software" +msgstr "phần mềm" + +#: src/main/extractor.c:71 src/main/extractor.c:64 +msgid "disclaimer" +msgstr "từ chối trách nhiệm" + +#: src/main/extractor.c:72 src/errs.c:88 src/gram.c:321 src/reduce.c:394 +#: src/main/extractor.c:65 lib/parsehelp.c:40 +msgid "warning" +msgstr "cảnh báo" + +#: src/main/extractor.c:73 src/main/extractor.c:66 +msgid "translated" +msgstr "dịch" + +#: src/main/extractor.c:75 src/main/extractor.c:68 +msgid "modification date" +msgstr "ngày sửa đổi" + +#: src/main/extractor.c:76 src/main/extractor.c:69 +msgid "creator" +msgstr "người tạo" + +#: src/main/extractor.c:77 src/main/extractor.c:70 +msgid "producer" +msgstr "người cung cấp" + +#: src/main/extractor.c:79 src/main/extractor.c:72 +msgid "page orientation" +msgstr "hướng trang" + +#: src/main/extractor.c:80 src/main/extractor.c:73 +msgid "paper size" +msgstr "cỡ giấy" + +#: src/main/extractor.c:81 src/main/extractor.c:74 +msgid "used fonts" +msgstr "phông chữ đã dùng" + +#: src/main/extractor.c:82 src/main/extractor.c:75 +msgid "page order" +msgstr "thứ tự trang" + +#: src/main/extractor.c:83 src/main/extractor.c:76 +msgid "created for" +msgstr "tạo cho" + +#: src/main/extractor.c:84 src/main/extractor.c:77 +msgid "magnification" +msgstr "phóng to" + +#: src/main/extractor.c:85 src/main/extractor.c:78 +msgid "release" +msgstr "bản phát hành" + +#: ../src/nautilus-file-management-properties.glade.h:82 +msgid "group" +msgstr "nhóm" + +#: ../providers/evolution/gda-calendar-model.c:62 +msgid "summary" +msgstr "tóm tắt" + +#: src/main/extractor.c:89 src/main/extractor.c:82 +msgid "packager" +msgstr "nhà đóng gói" + +#: lib/report.c:604 +msgid "vendor" +msgstr "nhà bán" + +#: src/main/extractor.c:91 gst/gsttag.c:148 src/main/extractor.c:84 +msgid "license" +msgstr "quyền phép" + +#: src/main/extractor.c:92 src/main/extractor.c:85 +msgid "distribution" +msgstr "bản phân phối" + +#: src/main/extractor.c:93 src/main/extractor.c:86 +msgid "build-host" +msgstr "máy hỗ trợ xây dụng" + +#: src/main/extractor.c:94 src/main/extractor.c:87 +msgid "os" +msgstr "hệ điều hành" + +#: src/main/extractor.c:95 src/main/extractor.c:88 +msgid "dependency" +msgstr "phụ thuộc" + +# Name: don't translate / Tên: đừng dịch +#: src/main/extractor.c:96 src/main/extractor.c:89 +msgid "MD4" +msgstr "MD4" + +# Name: don't translate / Tên: đừng dịch +#: src/main/extractor.c:97 src/main/extractor.c:90 +msgid "MD5" +msgstr "MD5" + +# Name: don't translate / Tên: đừng dịch +#: src/main/extractor.c:98 src/main/extractor.c:91 +msgid "SHA-0" +msgstr "SHA-0" + +# Name: don't translate / Tên: đừng dịch +#: src/main/extractor.c:99 src/main/extractor.c:92 +msgid "SHA-1" +msgstr "SHA-1" + +# Name: don't translate / Tên: đừng dịch +#: src/main/extractor.c:100 src/main/extractor.c:93 +msgid "RipeMD160" +msgstr "RipeMD160" + +#: src/main/extractor.c:101 src/main/extractor.c:94 +msgid "resolution" +msgstr "độ phân giải" + +#: src/main/extractor.c:102 src/main/extractor.c:95 +msgid "category" +msgstr "phân loại" + +#: src/main/extractor.c:103 src/main/extractor.c:96 +msgid "book title" +msgstr "tên sách" + +#: src/main/extractor.c:104 src/main/extractor.c:97 +msgid "priority" +msgstr "ưu tiên" + +#: src/main/extractor.c:105 src/main/extractor.c:98 +msgid "conflicts" +msgstr "xung đột" + +#: src/main/extractor.c:106 src/main/extractor.c:99 src/reason_fragment.cc:39 +#: dselect/pkgdisplay.cc:77 +msgid "replaces" +msgstr "thay thế" + +#: src/main/extractor.c:107 src/main/extractor.c:100 dselect/pkgdisplay.cc:76 +msgid "provides" +msgstr "cung cấp" + +#: src/main/extractor.c:108 src/main/extractor.c:101 +msgid "conductor" +msgstr "người chỉ huy" + +#: src/main/extractor.c:109 src/main/extractor.c:102 +msgid "interpreter" +msgstr "người dịch" + +#: src/main/extractor.c:110 src/main/extractor.c:103 +#: ../src/nautilus-file-management-properties.glade.h:88 +msgid "owner" +msgstr "sở hữu" + +#: src/main/extractor.c:111 src/main/extractor.c:104 +msgid "lyrics" +msgstr "lời bài hát" + +#: src/main/extractor.c:112 src/main/extractor.c:105 +msgid "media type" +msgstr "kiểu vật chứa" + +#: src/main/extractor.c:114 src/main/extractor.c:107 +msgid "binary thumbnail data" +msgstr "dữ liệu hình thu nhỏ nhị phân" + +#: src/main/extractor.c:115 src/main/extractor.c:108 +msgid "publication date" +msgstr "ngày xuất bản" + +#: src/main/extractor.c:116 +msgid "camera make" +msgstr "nhà chế tạo máy ảnh" + +#: src/main/extractor.c:117 +msgid "camera model" +msgstr "mô hình máy ảnh" + +#: src/main/extractor.c:118 +msgid "exposure" +msgstr "sự phơi nắng" + +#: src/main/extractor.c:119 +msgid "aperture" +msgstr "lỗ ống kính" + +#: src/main/extractor.c:120 +msgid "exposure bias" +msgstr "khuynh hướng phơi nắng" + +#: src/main/extractor.c:121 libexif/exif-entry.c:487 +msgid "flash" +msgstr "đèn nháy" + +#: src/main/extractor.c:122 +msgid "flash bias" +msgstr "khuynh hướng đèn nháy" + +#: src/main/extractor.c:123 +msgid "focal length" +msgstr "tiêu cự" + +#: src/main/extractor.c:124 +msgid "focal length (35mm equivalent)" +msgstr "tiêu dự (35mm tương đương)" + +#: src/main/extractor.c:125 +msgid "iso speed" +msgstr "tốc độ ISO" + +#: src/main/extractor.c:126 +msgid "exposure mode" +msgstr "chế độ phơi nắng" + +#: src/main/extractor.c:127 +msgid "metering mode" +msgstr "chế độ do" + +#: src/main/extractor.c:128 +msgid "macro mode" +msgstr "chế độ macrô" + +#: src/main/extractor.c:129 +msgid "image quality" +msgstr "chất lượng ảnh" + +#: src/main/extractor.c:130 +msgid "white balance" +msgstr "cán cân trắng" + +#: src/main/extractor.c:131 +msgid "orientation" +msgstr "hướng" + +#: src/main/extractor.c:132 +msgid "template" +msgstr "mẫu" + +#: src/main/extractor.c:226 src/main/extractor.c:194 +#, c-format +msgid "Initialization of plugin mechanism failed: %s!\n" +msgstr "Việc khởi động cơ chế cầm phít bị lỗi: %s\n" + +#: src/main/extractor.c:375 +#, c-format +msgid "" +"Resolving symbol `%s' in library `%s' failed, so I tried `%s', but that " +"failed also. Errors are: `%s' and `%s'.\n" +msgstr "" +"Việc tháo gỡ ký hiệu « %s » trong thư viên « %s » bị lỗi, thì đã cố « %s », " +"nhưng mà nó cũng không thành công. Gặp lỗi « %s » và « %s ».\n" + +#: src/main/extractor.c:404 +#, c-format +msgid "Loading `%s' plugin failed: %s\n" +msgstr "Việc tải bộ cầm phít « %s » bị lỗi: %s\n" + +#: src/main/extractor.c:609 +#, c-format +msgid "Unloading plugin `%s' failed!\n" +msgstr "Việc bỏ tải bộ cầm phít « %s » bị lỗi.\n" + +#: ../src/gam-app.c:95 ../src/ghex-ui.xml.h:18 +msgid "E_xit" +msgstr "T_hoát" + +#: ../gnibbles/gnibbles.soundlist.in.h:5 ../gnometris/field.cpp:130 +msgid "Game Over" +msgstr "Hết lượt chơi" + +#: doc/demux_nsf.c:320 +#, c-format +msgid "demux_nsf.c: input not seekable, can not handle!\n" +msgstr "demux_nsf.c: không thể tìm trong dữ liệu gõ nên không thể quản lý!\n" + +#: doc/demux_wc3movie.c:210 +#, c-format +msgid "demux_wc3movie: SHOT chunk referenced invalid palette (%d >= %d)\n" +msgstr "" +"demux_wc3movie: phần riêng SHOT đã tham chiếu đến bảng chọn không hợp lệ (%d " +"≥ %d)\n" + +#: doc/demux_wc3movie.c:300 doc/demux_wc3movie.c:538 +#, c-format +msgid "demux_wc3movie: encountered unknown chunk: %c%c%c%c\n" +msgstr "demux_wc3movie: gặp phần riêng lạ: %c%c%c%c\n" + +#: doc/demux_wc3movie.c:449 +msgid "demux_wc3movie: There was a problem while loading palette chunks\n" +msgstr "demux_wc3movie: gặp lỗi trong khi tải các phần riêng bảng chọn\n" + +#: src/plugins/htmlextractor.c:130 src/plugins/htmlextractor.c:928 +#, c-format +msgid "Fatal: could not allocate (%s at %s:%d).\n" +msgstr "Nghiêm trọng: không thể cấp phát (%s lúc %s.%d).\n" + +#: src/buffer.c:67 +msgid "any type" +msgstr "bất cứ kiểu nào" + +#: lib/routines.c:160 lib/xbackupfile.c:248 lib/xbackupfile.c:276 +#: lib/xbackupfile.c:284 src/delegate.c:260 +#, c-format +msgid "cannot create file `%s'" +msgstr "không thể tạo tập tin « %s »" + +#: lib/routines.c:190 lib/routines.c:196 src/delegate.c:269 src/select.c:159 +#, c-format +msgid "cannot open a pipe on `%s'" +msgstr "không thể mở ống dẫn trên « %s »" + +#. E.g.: Delegation `PsNup', from ps to ps +#: src/delegate.c:389 +#, c-format +msgid "Delegation `%s', from %s to %s\n" +msgstr "Ủy quyền « %s », từ %s cho %s\n" + +#: src/delegate.c:408 src/delegate.c:430 +msgid "Applications configured for delegation" +msgstr "Ứng dụng có cấu hình để ủy quyền" + +#: src/generate.c:88 +#, c-format +msgid "`%s' is a directory" +msgstr "« %s » là một thư mục" + +#: lib/confg.c:288 lib/confg.c:451 lib/routines.c:154 src/generate.c:96 +#: src/main.c:558 src/main.c:580 src/files.c:101 +#, c-format +msgid "cannot open file `%s'" +msgstr "không thể mở tập tin « %s »." + +#. Another kind of error occurred: exit +#: lib/xbackupfile.c:224 src/generate.c:101 +#, c-format +msgid "cannot get informations on file `%s'" +msgstr "không thể lấy thông tin về tập tin « %s »" + +#: src/generate.c:168 +#, c-format +msgid "[%s (%s): 1 page on 1 sheet]\n" +msgstr "[%s (%s): 1 trang trên 1 lá]\n" + +#: src/generate.c:174 +#, c-format +msgid "[%s (%s): %d pages on 1 sheet]\n" +msgstr "[%s (%s): %d trang trên 1 lá]\n" + +#: src/generate.c:181 +#, c-format +msgid "[%s (%s): %d pages on %d sheets]\n" +msgstr "[%s (%s): %d trang trên %d lá]\n" + +#: src/generate.c:208 +#, c-format +msgid "[Total: 1 page on 1 sheet] %s\n" +msgstr "[Tổng số : 1 trang trên 1 lá] %s\n" + +#: src/generate.c:212 +#, c-format +msgid "[Total: %d pages on 1 sheet] %s\n" +msgstr "[Tổng số : %d trang trên 1 lá] %s\n" + +#: src/generate.c:217 +#, c-format +msgid "[Total: %d pages on %d sheets] %s\n" +msgstr "[Tổng số : %d trang trên %d lá] %s\n" + +#: src/generate.c:226 +msgid "[1 line wrapped]\n" +msgstr "[1 dòng đã ngắt]\n" + +#: src/generate.c:229 +#, c-format +msgid "[%d lines wrapped]\n" +msgstr "[%d dòng đã ngắt]\n" + +#: src/generate.c:242 +msgid "[No output produced]\n" +msgstr "[Chưa xuất gì]\n" + +#: src/generate.c:314 +#, c-format +msgid "%s, delegated to %s" +msgstr "%s, ủy quyền cho %s" + +#: src/generate.c:322 +#, c-format +msgid "[%s (%s): failed. Ignored]\n" +msgstr "[%s (%s): thất bại nên bị bo qua.]\n" + +#: src/generate.c:330 +#, c-format +msgid "[%s (unprintable): ignored]\n" +msgstr "[%s (không thể in ra được): nên bị bỏ qua]\n" + +#: src/generate.c:339 +#, c-format +msgid "[%s (binary): ignored]\n" +msgstr "[%s (nhị phân): nên bị bỏ qua]\n" + +#: src/generate.c:360 +msgid "plain" +msgstr "thuần" + +#: /home/akim/src/a2ps-4.12/src/lexssh.l:348 +msgid "end-of-line in string constant" +msgstr "kết thúc dòng trong hằng số chuỗi" + +#. TRANS: %s is ".." or <..> or /../ etc. +#: src/sheets-map.l:191 +#, c-format +msgid "end of line inside a %s" +msgstr "kết thúc dòng ở trong %s" + +#: src/main.c:201 +#, c-format +msgid "received signal %d: %s" +msgstr "nhận tín hiệu %d: %s" + +#. TRANS: highlighting level = heavy (2/2) +#: src/main.c:240 +msgid "heavy" +msgstr "nặng" + +#: ../srcore/verbose.xml.in.h:41 libexif/canon/mnote-canon-entry.c:75 +#: libexif/canon/mnote-canon-entry.c:108 libexif/canon/mnote-canon-entry.c:111 +#: libexif/canon/mnote-canon-entry.c:114 +#: libexif/olympus/mnote-olympus-entry.c:378 +#, fuzzy +msgid "normal" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"thường\n" +"#-#-#-#-# libexif-0.6.13.vi.po (libexif-0.6.13) #-#-#-#-#\n" +"chuẩn" + +#: src/main.c:333 +#, c-format +msgid "Configuration status of %s %s\n" +msgstr "Tính trạng cấu hình của %s %s\n" + +#: src/main.c:337 src/main.c:694 +msgid "Sheets:\n" +msgstr "Tờ giấy:\n" + +#: src/main.c:338 +#, c-format +msgid "" +" medium = %s%s, %s\n" +" page layout = %d x %d, %s\n" +" borders = %s\n" +" file alignment = %s\n" +" interior margin = %d\n" +msgstr "" +" vật vật chứa = %s%s, %s\n" +" bố cục trang = %d x %d, %s\n" +" viền = %s\n" +" canh lề tập tin = %s\n" +" lề ở trong = %d\n" + +#: src/main.c:347 +msgid "portrait" +msgstr "thẳng đứng" + +#: src/main.c:347 libexif/canon/mnote-canon-entry.c:98 +msgid "landscape" +msgstr "nằm ngang" + +#: src/main.c:356 +#, c-format +msgid "%d characters per line" +msgstr "%d ký tự trong mỗi dòng" + +#: src/main.c:359 +#, c-format +msgid "%d lines per page" +msgstr "%d dòng trong mỗi trang" + +#: src/main.c:362 +#, c-format +msgid "font size is %gpt" +msgstr "cỡ phông chữ là %gpt" + +#. number line: each line +#: src/main.c:371 +msgid "each line" +msgstr "mỗi dòng" + +#. number line: each %d line +#: src/main.c:375 +#, c-format +msgid "each %d lines" +msgstr "mỗi %d dòng" + +#: src/main.c:378 src/main.c:715 +msgid "Virtual pages:\n" +msgstr "Trang ảo :\n" + +#: src/main.c:379 +#, c-format +msgid "" +" number lines = %s\n" +" format = %s\n" +" tabulation size = %d\n" +" non printable format = %s\n" +msgstr "" +" dòng số = %s\n" +" dạng = %s\n" +" cỡ tab = %d\n" +" dạng khi không in = %s\n" + +#: src/main.c:390 +msgid "Headers:\n" +msgstr "Dầu trang:\n" + +#: src/main.c:391 +#, c-format +msgid "" +" header = %s\n" +" left footer = %s\n" +" footer = %s\n" +" right footer = %s\n" +" left title = %s\n" +" center title = %s\n" +" right title = %s\n" +" under lay = %s\n" +msgstr "" +" đầu trang = %s\n" +" chân trang bên trái = %s\n" +" chân trang = %s\n" +" chân trang bên phải = %s\n" +" đầu đề bên trái = %s\n" +" đầu đề ở trung tâm = %s\n" +" đầu đề bên phải = %s\n" +" giấy lót = %s\n" + +#: src/main.c:410 src/main.c:744 +msgid "Input:\n" +msgstr "Nhập :\n" + +#: src/main.c:411 +#, c-format +msgid "" +" truncate lines = %s\n" +" interpret = %s\n" +" end of line = %s\n" +" encoding = %s\n" +" document title = %s\n" +" prologue = %s\n" +" print anyway = %s\n" +" delegating = %s\n" +msgstr "" +" cắt bớt dòng = %s\n" +" giải thích = %s\n" +" kết thúc dòng = %s\n" +" mã ký tự = %s\n" +" đầu đề tài liệu = %s\n" +" đoạn mở đầu = %s\n" +" in bất chấp = %s\n" +" ủy quyền = %s\n" + +#. TRANS: a2ps -E --list=options. Warning, this answer is also +#. used for the PPD file. Make it compatible with both. +#: src/main.c:436 src/main.c:502 +msgid "selected automatically" +msgstr "tự động chọn" + +#: src/main.c:439 src/main.c:763 +msgid "Pretty-printing:\n" +msgstr "In xinh:\n" + +#: src/main.c:440 +#, c-format +msgid "" +" style sheet = %s\n" +" highlight level = %s\n" +" strip level = %d\n" +msgstr "" +" tờ kiểu dáng = %s\n" +" mức nổi bật = %s\n" +" mức tước = %d\n" + +#: src/main.c:460 +msgid "never make backups" +msgstr "không bao giờ sao lưu tập tin" + +#: src/main.c:464 +msgid "simple backups of every file" +msgstr "bản sao lưu đơn giản của mọi tập tin" + +#. appears in a2ps --version-=existing --list=defaults +#: src/main.c:469 +msgid "" +"numbered backups of files already numbered,\n" +" and simple of others" +msgstr "" +"bản sao lưu đánh số của tâp tin đã đánh số,\n" +" và bản sao đơn giản của các tập tin khác" + +#: src/main.c:474 +msgid "numbered backups of every file" +msgstr "bản sao lưu đánh số của mọi tập tin" + +#: src/main.c:478 src/main.c:772 +msgid "Output:\n" +msgstr "Xuất:\n" + +#: src/main.c:479 +#, c-format +msgid "" +" destination = %s\n" +" version control = %s\n" +" backup suffix = %s\n" +msgstr "" +" nơi nhận = %s\n" +" điều khiển phiên bản = %s\n" +" hậu tố sao lưu = %s\n" + +#: src/main.c:492 src/main.c:782 +msgid "PostScript:\n" +msgstr "PostScript:\n" + +#: src/main.c:493 +#, c-format +msgid "" +" magic number = %s\n" +" Printer Description (PPD) = %s\n" +" default PPD = %s\n" +" page label format = %s\n" +" number of copies = %d\n" +" sides per sheet = %s\n" +" page device definitions = " +msgstr "" +" số mã thuật = %s\n" +" Mô tả máy in (PPD) = %s\n" +" (Mô tả máy in) PPD mặc định = %s\n" +" dạng nhãn trang = %s\n" +" số bản = %d\n" +" mặt của một tờ giấy = %s\n" +" định nghĩa thiết bị trang = " + +#: src/main.c:513 +msgid " statusdict definitions = " +msgstr " định nghĩa statusdict (từ điển tính trạng) = " + +#: src/main.c:516 +#, c-format +msgid " page prefeed = %s\n" +msgstr " nạp giấy trước = %s\n" + +#: src/main.c:525 +msgid "Internals:\n" +msgstr "Chi tiết nội bộ :\n" + +#: src/main.c:526 +#, c-format +msgid "" +" verbosity level = %d\n" +" file command = %s\n" +" library path = \n" +msgstr "" +" mức xuất chi tiết = %d\n" +" lệnh tập tin = %s\n" +" đường dẫn thư viện = \n" + +#: src/main.c:651 +#, c-format +msgid "" +"Usage: %s [OPTION]... [FILE]...\n" +"\n" +"Convert FILE(s) or standard input to PostScript.\n" +"\n" +"Mandatory arguments to long options are mandatory for short options too.\n" +"Long options marked with * require a yes/no argument, corresponding\n" +"short options stand for `yes'.\n" +msgstr "" +"Cách sử dụng: %s [TÙY_CHỌN]... [TẬP_TIN]...\n" +"\n" +"Chuyển đổi TẬP_TIN hay dữ liệu gõ chuẩn sang PostScript.\n" +"\n" +"Mọi đối số phải sử dụng với tùy chọn dài cũng vậy với tùy chọn ngắn.\n" +"Mọi tùy chọn dài có dấu * phải có đối số Có/Không (yes/no);\n" +"tùy chọn ngắn tương thích thì có nghĩa Có (yes).\n" + +#: src/main.c:665 +msgid "Tasks:\n" +msgstr "Việc:\n" + +#: src/main.c:666 +msgid "" +" --version display version\n" +" --help display this help\n" +" --guess report guessed types of FILES\n" +" --which report the full path of library files named FILES\n" +" --glob report the full path of library files matching FILES\n" +" --list=defaults display default settings and parameters\n" +" --list=TOPIC detailed list on TOPIC (delegations, encodings, " +"features,\n" +" variables, media, ppd, printers, prologues, style-" +"sheets,\n" +" user-options)\n" +msgstr "" +" --version trình bày thông tin _phiên bản_\n" +" --help trình bày _trợ giúp_ này\n" +" --guess thông báo loại đã _đoán_ của TẬP_TIN\n" +" --which thông báo đường dẫn đầy đủ của mọi tập tin thư viện có tên " +"TẬP_TIN (_nào_)\n" +" --glob thông báo đường dẫn đầy đủ của mọi tập tin thư viện khớp " +"với TẬP_TIN\n" +" --list=defaults _ghi danh sách_ các thiết bị và tham số _mặc định_\n" +" --list=ĐỀ_TÀI _danh sách_ chi tiết về ĐỀ_TÀI đó (ủy quyền gì, mã ký " +"tự, tính năng,\n" +"\t\tbiến, vật vật chứa, mô tả máy in (PPD), máy in, đoạn mở đầu, tờ kiểu " +"dang,\n" +"\t\ttùy chọn cho người dùng)\n" + +#: src/main.c:677 +msgid "" +"After having performed the task, exit successfully. Detailed lists may\n" +"provide additional help on specific features.\n" +msgstr "" +"Sau khi thực hiện việc đó hãy thoát được. Danh sách chi tiết có lẽ\n" +"bao gồm trợ giúp thêm về tính năng dứt khoát.\n" + +#: src/main.c:685 +msgid "Global:\n" +msgstr "Toàn cục:\n" + +#: src/main.c:686 +msgid "" +" -q, --quiet, --silent be really quiet\n" +" -v, --verbose[=LEVEL] set verbosity on, or to LEVEL\n" +" -=, --user-option=OPTION use the user defined shortcut OPTION\n" +" --debug enable debugging features\n" +" -D, --define=KEY[:VALUE] unset variable KEY or set to VALUE\n" +msgstr "" +" -q, --quiet, --silent hãy _im_ lắm (không xuất chi tiết)\n" +" -v, --verbose[=MỨC] xuất _chi tiết_, hay xuất chi tiết MỨC đó\n" +" -=, --user-option=TÙY_CHỌN sử dụng _tùy chọn_ lối tắt định nghĩa do " +"_người dùng_\n" +" --debug hiệu lực tính năng _gỡ lỗi_\n" +" -D, --define=PHÍM[:GIÁ_TRỊ] bỏ lập PHÍM biến hay lập thành GIÁ TRỊ đó\n" + +#: src/main.c:695 +msgid "" +" -M, --medium=NAME use output medium NAME\n" +" -r, --landscape print in landscape mode\n" +" -R, --portrait print in portrait mode\n" +" --columns=NUM number of columns per sheet\n" +" --rows=NUM number of rows per sheet\n" +" --major=DIRECTION first fill (DIRECTION=) rows, or columns\n" +" -1, -2, ..., -9 predefined font sizes and layouts for 1.. 9 " +"virtuals\n" +" -A, --file-align=MODE align separate files according to MODE (fill, rank\n" +" page, sheet, or a number)\n" +" -j, --borders* print borders around columns\n" +" --margin[=NUM] define an interior margin of size NUM\n" +msgstr "" +" -M, --medium=TÊN sử dụng _vật vật chứa_ có TÊN đó\n" +" -r, --landscape in bằng chế độ _ngang_\n" +" -R, --portrait in bằng chế độ _chân dung_\n" +" --columns=SỐ số _cột_ trên một tờ giấy\n" +" --rows=SỐ số _hàng_ trên một tờ giấy\n" +" --major=HƯỚNG trước hết tô đầy hàng hay cột HƯỚNG đó (nhiều hơn)\n" +" -1, -2, ..., -9 cỡ phông chữ và bố trí định nghĩa trước cho điều ảo " +"1..9 \n" +" -A, --file-align=CHẾ_ĐỘ _canh lề_ những _tập tin_ riêng theo CHẾ ĐỘ đó\n" +"\t\t\t(fill [tô đầy], rank page [sắp xếp trang], sheet [tờ giấy] hay số) -" +"j, --borders* in _viền_ chung quanh cột\n" +" --margin[=SỐ] định nghĩa _lề trang_ nội bộ có kích thước SỐ đó\n" + +#: src/main.c:708 +msgid "" +"The options -1.. -9 affect several primitive parameters to set up " +"predefined\n" +"layouts with 80 columns. Therefore the order matters: `-R -f40 -2' is\n" +"equivalent to `-2'. To modify the layout, use `-2Rf40', or compose " +"primitive\n" +"options (`--columns', `--font-size' etc.).\n" +msgstr "" +"Những tùy chọn -1.. -9 làm ảnh hưởng đến vài tham số nguyên thuỷ\n" +"để thiết lập bố trí định nghĩa trước có 80 cột. Vì thế thứ tự là quan " +"trọng:\n" +"`-R -f40 -2' bằng `-2'. Để sửa đổi bố trí thì hãy sư dụng `-2Rf40',\n" +"hay tạo tùy chọn nguyên thuỷ (`--columns' [cột], `--font-size' [cỡ phông " +"chữ] v.v.).\n" + +#: src/main.c:716 +msgid "" +" --line-numbers=NUM precede each NUM lines with its line number\n" +" -C alias for --line-numbers=5\n" +" -f, --font-size=SIZE use font SIZE (float) for the body text\n" +" -L, --lines-per-page=NUM scale the font to print NUM lines per virtual\n" +" -l, --chars-per-line=NUM scale the font to print NUM columns per " +"virtual\n" +" -m, --catman process FILE as a man page (same as -L66)\n" +" -T, --tabsize=NUM set tabulator size to NUM\n" +" --non-printable-format=FMT specify how non-printable chars are printed\n" +msgstr "" +" --line-numbers=SỐ chèn _số dòng_ trước mỗi dòng thứ SỐ\n" +" -C\t\t\t\t\t biệt hiệu cho tùy chọn --line-numbers=5\n" +" -f, --font-size=CỠ sử dụng _CỠ phông chữ_ (nổi) khi in chữ nội " +"dụng\n" +" -L, --lines-per-page=SỐ co giãn phông chữ để in SỐ _dòng trong mỗi " +"trang_ ảo\n" +" -l, --chars-per-line=SỐ cơ giãn phông chữ để in SỐ _cột trong mỗi_ trang " +"ảo (_dòng_)\n" +" -m, --catman xử lý TẬP_TIN dạng trang « man » (bằng tùy " +"chọn -L66)\n" +" -T, --tabsize=SỐ lập _cỡ « tab»_ thành SỐ\n" +" --non-printable-format=DẠNG ghi rõ cách in mọi ký tự _không thể in_\n" + +#: src/main.c:727 +msgid "Headings:\n" +msgstr "Tựa đề:\n" + +#: src/main.c:729 +#, no-c-format +msgid "" +" -B, --no-header no page headers at all\n" +" -b, --header[=TEXT] set page header\n" +" -u, --underlay[=TEXT] print TEXT under every page\n" +" --center-title[=TEXT] set page title to TITLE\n" +" --left-title[=TEXT] set left and right page title to TEXT\n" +" --right-title[=TEXT]\n" +" --left-footer[=TEXT] set sheet footers to TEXT\n" +" --footer[=TEXT]\n" +" --right-footer[=TEXT]\n" +msgstr "" +" -B, --no-header _không có đầu trang_ nào cả\n" +" -b, --header[=CHỮ] lập _đầu trang_\n" +" -u, --underlay[=CHỮ] in CHỮ dưới mọi trang (_giấy lót_)\n" +" --center-title[=CHỮ] lập _đầu đề_ trang thành CHỮ (_trung tâm_)\n" +" --left-title[=CHỮ] lập _đầu đề bên trái_ trang thành CHỮ\n" +" --right-title[=CHỮ] lập _đầu đề bên phải_ trang thành CHỮ\n" +" --left-footer[=CHỮ] lập _chân bên trái_ trang thành CHỮ\n" +" --footer[=CHỮ] lập _chân trang_ thành CHỮ\n" +" --right-footer[=CHỮ] lập _chân bên phải_ trang thành CHỮ\n" + +#: src/main.c:740 +msgid "The TEXTs may use special escapes.\n" +msgstr "CHỮ đó có thể sử dụng ký tự thoát đặc biệt.\n" + +#: src/main.c:745 +msgid "" +" -a, --pages[=RANGE] select the pages to print\n" +" -c, --truncate-lines* cut long lines\n" +" -i, --interpret* interpret tab, bs and ff chars\n" +" --end-of-line=TYPE specify the eol char (TYPE: r, n, nr, rn, any)\n" +" -X, --encoding=NAME use input encoding NAME\n" +" -t, --title=NAME set the name of the job\n" +" --stdin=NAME set the name of the input file stdin\n" +" --print-anyway* force binary printing\n" +" -Z, --delegate* delegate files to another application\n" +" --toc[=TEXT] generate a table of content\n" +msgstr "" +" -a, --pages[=PHẠM_VỊ] chọn _trang_ nào để in\n" +" -c, --truncate-lines* _cắt bớt_ mọi _dòng_ dài\n" +" -i, --interpret* _giải thích_ mọi ký tự tab, xoá lùi và nạp " +"giấy\n" +" --end-of-line=LOẠI ghi rõ ký tự _kết thúc dòng_\n" +"\t\t\t\t(LOẠI: r, n, nr, rn, any [bất cứ ký tự nào])\n" +" -X, --encoding=TÊN sư dụng _mã_ ký tự gõ TÊN này\n" +" -t, --title=TÊN lập _tên_ của việc này\n" +" --stdin=TÊN lập TÊN của tập tin _gõ chuẩn_\n" +" --print-anyway* buộc in cách nhị phân\n" +" -Z, --delegate* _ủy quyền_ tập tin cho ứng dụng khác\n" +" --toc[=CHỮ] tạo _mục lục_\n" + +#: src/main.c:757 +msgid "" +"When delegations are enabled, a2ps may use other applications to handle the\n" +"processing of files that should not be printed as raw information, e.g., " +"HTML\n" +"PostScript, PDF etc.\n" +msgstr "" +"Khi tùy chọn « ủy quyền » là hoặt động, trình a2ps có lẽ sử dụng ứng dụng " +"khác\n" +"để xử lý tâp tin không in được dạng dữ liệu thô, v.d. HTML, PostScript, " +"PDF.\n" + +#: src/main.c:764 +msgid "" +" -E, --pretty-print[=LANG] enable pretty-printing (set style to LANG)\n" +" --highlight-level=LEVEL set pretty printing highlight LEVEL\n" +" LEVEL can be none, normal or heavy\n" +" -g alias for --highlight-level=heavy\n" +" --strip-level=NUM level of comments stripping\n" +msgstr "" +" -E, --pretty-print[=NGÔN_NGỮ] hiệu lực _in xinh_ (lập kiểu thành NGÔN " +"NGỮ)\n" +" --highlight-level=MỨC lập _mức nổi_ khi in xinh\n" +" MỨC có thể là none [không có], normal [thường] " +"hay heavy [nặng]\n" +" -g biệt hiệu cho tùy chọn --highlight-level=heavy " +"(mức nổi là nặng)\n" +" --strip-level=SỐ _mức tước_ chú thích\n" + +#: src/main.c:773 +msgid "" +" -o, --output=FILE leave output to file FILE. If FILE is `-',\n" +" leave output to stdout.\n" +" --version-control=WORD override the usual version control\n" +" --suffix=SUFFIX override the usual backup suffix\n" +" -P, --printer=NAME send output to printer NAME\n" +" -d send output to the default printer\n" +msgstr "" +" -o, --output=TẬP_TIN _xuất_ đến tập tin đó; nếu tập tin đó là " +"`-',\n" +" thì xuất đến thiết bị xuất chuẩn (stdout).\n" +" --version-control=TỪ có quyền cao hơn _điều khiển phiên bản_ thường\n" +" --suffix=HẬU_TỐ có quyền cao hơn _hậu tố_ sao lưu thường\n" +" -P, --printer=TÊN xuất đến _máy in_ có tên đó\n" +" -d xuất đến may in mặc định\n" + +#: src/main.c:783 +msgid "" +" --prologue=FILE include FILE.pro as PostScript prologue\n" +" --ppd[=KEY] automatic PPD selection or set to KEY\n" +" -n, --copies=NUM print NUM copies of each page\n" +" -s, --sides=MODE set the duplex MODE (`1' or `simplex',\n" +" `2' or `duplex', `tumble')\n" +" -S, --setpagedevice=K[:V] pass a page device definition to output\n" +" --statusdict=K[:[:]V] pass a statusdict definition to the output\n" +" -k, --page-prefeed enable page prefeed\n" +" -K, --no-page-prefeed disable page prefeed\n" +msgstr "" +" --prologue=TẬP_TIN bao gồm TẬP_TIN.pro là _đoạn mở đầu_ " +"PostScript\n" +" --ppd[=PHÍM] tự động chọn mô tả may in (PPD) hay lập thành " +"PHÍM đó\n" +" -n, --copies=SỐ in SỐ _bản sao_ của mỗi trang\n" +" -s, --sides=CHẾ_ĐỘ lập chế độ _mặt trang_ (`1' hay `simplex',\n" +" `2' hay `duplex', `tumble')\n" +" -S, --setpagedevice=K[:V] xuất dữ liệu định nghĩa _thiết bị trang_ " +"(_lập_)\n" +" --statusdict=K[:[:]V] xuất dữ liệu định nghĩa statusdict (từ điển " +"tính trạng)\n" +" -k, --page-prefeed hiệu lực _nạp trang trước_\n" +" -K, --no-page-prefeed vô hiệu hóa _nạp trang trước_ (_không_)\n" + +#: src/main.c:797 +msgid "" +"By default a2ps is tuned to do what you want to, so trust it. To pretty\n" +"print the content of the `src' directory and a table of content, and send " +"the\n" +"result to the printer `lw',\n" +"\n" +" $ a2ps -P lw --toc src/*\n" +"\n" +"To process the files `sample.ps' and `sample.html' and display the result,\n" +"\n" +" $ a2ps -P display sample.ps sample.html\n" +"\n" +"To process a mailbox in 4 up,\n" +"\n" +" $ a2ps -=mail -4 mailbox\n" +"\n" +"To print as a booklet on the default printer, which is Duplex capable,\n" +"\n" +" $ a2ps -=book paper.dvi.gz -d\n" +msgstr "" +"Mặc định là trình a2ps sẽ giúp đỡ bạn làm việc thì hãy tin nó để làm việc " +"cho đúng.\n" +"\n" +"Để « in xinh » nội dung của thư mục `src', và _mục lục_,\n" +"\trồi gởi kết quả đó cho máy in `lw' hãy sử dụng lệnh này:\n" +"\n" +" $ a2ps -P lw --toc src/*\n" +"\n" +"Để xử lý hai tập tin `sample.ps' và `sample.html' rồi _trình bày_ kết quả " +"ấy\n" +"\tthì hãy sử dụng lệnh này:\n" +"\n" +" $ a2ps -P display sample.ps sample.html\n" +"\n" +"Để xử lý một _hộp thư_ để xuất bốn _thư_ trên mỗi tờ giấy (4 up)\n" +"\tthì hãy sử dụng lệnh này:\n" +"\n" +" $ a2ps -=mail -4 mailbox\n" +"\n" +"Để in dạng _cuốn sách_ nhỏ qua máy in mặc định mà có thể in hai mặt _tờ " +"giấy_\n" +"thì hãy sư dụng lệnh này:\n" +"\n" +" $ a2ps -=book paper.dvi.gz -d\n" + +#: src/main.c:818 +msgid "" +"News, updates and documentation: visit http://www.inf.enst.fr/~demaille/" +"a2ps/.\n" +msgstr "" +"Để xem tin tức, trình cập nhật và tài liệu thì hãy tới thăm chỗ Mạng của " +"chúng tôi nhé:\n" +"\thttp://www.inf.enst.fr/~demaille/a2ps/.\n" + +#: src/main.c:820 +msgid "Report bugs to .\n" +msgstr "Hãy thông báo lỗi cho .\n" + +#: src/main.c:938 +msgid "" +"Copyright (c) 1988-1993 Miguel Santana\n" +"Copyright (c) 1995-2000 Akim Demaille, Miguel Santana" +msgstr "" +"Bản quyền © năm 1988-1993 Miguel Santana\n" +"Bản quyền © năm 1995-2000 Akim Demaille, Miguel Santana" + +#: src/main.c:1168 +msgid "Table of Content" +msgstr "Mục lục" + +#: src/parsessh.y:236 +#, c-format +msgid "cannot process `%s' which requires a2ps version %s" +msgstr "không xử lý được « %s » mà cần đến trình a2ps phiên bản %s" + +#: src/sheets-map.l:110 +#, c-format +msgid "unexpected character `%c'" +msgstr "ký tự bất ngờ « %c »" + +#: /home/akim/src/a2ps-4.12/lib/lexppd.l:211 src/sheets-map.l:210 +msgid "too many includes" +msgstr "quá nhiều tập tin bao gồm (include)" + +#: src/sheets-map.l:292 src/sheets-map.l:299 +#, c-format +msgid "no key defined for `%s'" +msgstr "chưa định nghĩa phím cho « %s »" + +#: lib/pathwalk.c:414 src/select.c:122 src/ssheet.c:866 +#, c-format +msgid "cannot find file `%s'" +msgstr "không tìm thấy tâp tin « %s »" + +#. sheets.map can not be found: there is no automatic prettyprinting +#: src/select.c:124 +msgid "automatic style selection cancelled" +msgstr "việc tự động chọn kiểu dáng bị thôi" + +#: src/ssheet.c:295 +#, c-format +msgid "cannot compile regular expression `%s': %s" +msgstr "không thể biên dịch biểu thức chính quy « %s »: %s" + +#: src/ssheet.c:974 src/ssheet.c:995 +msgid "Known Style Sheets" +msgstr "Biết tờ kiểu dáng" + +#: src/ssheet.c:1461 +#, c-format +msgid "cannot find style sheet `%s': using plain style" +msgstr "không tìm thấy tờ kiểu dáng « %s » nên sử dụng kiểu dáng thuần" + +#: src/sshread.c:453 +#, c-format +msgid "unknown encoding `%s', ignored" +msgstr "không biết mã ký tự « %s » nên bỏ qua nó" + +#: src/version-etc.c:38 +msgid "Copyright (C) 1999 Free Software Foundation, Inc." +msgstr "" +"Bản quyền © năm 1999 Free Software Foundation, Inc. (Tổ chức Phần mềm Tự do)" + +#: lib/version-etc.c:90 +#, c-format +msgid "Written by %s.\n" +msgstr "Tác giả: %s.\n" + +#: src/wdiff.c:1225 src/getargs.c:275 schroot/schroot.c:108 +#: schroot/schroot.cc:50 schroot/schroot-releaselock.cc:58 +msgid "" +"This is free software; see the source for copying conditions. There is NO\n" +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" +msgstr "" +"Đây là phần mềm tự do; hãy xem mã nguồn để tìm thấy điều kiện sao chép.\n" +"• Không bảo đảm gì cả, dù khă nang bán hay khả năng làm việc dứt khoát. •\n" + +#: src/versions.c:133 +#, c-format +msgid "invalid version number `%s'" +msgstr "số phiên bản không hợp lệ « %s »" + +#: lib/caret.c:43 +msgid "space (i.e., ` ')" +msgstr "ký tự cách « »" + +#: lib/caret.c:45 +msgid "octal (i.e., `\\001' etc.)" +msgstr "bát phân (v.d. \\001)" + +#: lib/caret.c:47 +msgid "hexadecimal (i.e., `\\x0a' etc.)" +msgstr "hệ thập lục phân (v.d. \\x0a)" + +#: lib/caret.c:49 +msgid "caret (i.e., `^C', `M-^C' etc.)" +msgstr "dấu sót (v.d. ^C, M-^C)" + +#: lib/caret.c:51 +msgid "emacs (i.e., `C-c', `M-C-c' etc.)" +msgstr "emacs (v.d. C-c, M-C-c)" + +#: lib/caret.c:53 +msgid "question-mark (i.e., `?')" +msgstr "dấu hỏi (v.d. ?)" + +#: ../gettext-tools/lib/closeout.c:64 lib/closeout.c:64 +#: ../su-backend/closeout.c:71 ../su-backend/closeout.c:73 lib/closeout.c:94 +#: misc.c:853 +msgid "write error" +msgstr "lỗi ghi" + +#: lib/confg.c:253 lib/encoding.c:639 +#, c-format +msgid "invalid option `%s'" +msgstr "tùy chọn không hợp lệ `%s'" + +#: lib/confg.c:264 lib/encoding.c:577 lib/encoding.c:617 lib/prolog.c:146 +#, c-format +msgid "missing argument for `%s'" +msgstr "thiếu đối số cho `%s'" + +#: lib/confg.c:320 lib/confg.c:327 lib/confg.c:334 +#, c-format +msgid "invalid definition for printer `%s': %s" +msgstr "dữ liệu định nghĩa không hợp lệ cho máy in `%s': %s" + +#: lib/confg.c:325 lib/confg.c:328 +msgid "Unknown Printer" +msgstr "Không biết máy in" + +#: lib/confg.c:332 lib/confg.c:335 ../gnome-default-printer.c:231 +#: ../gnome-default-printer.desktop.in.h:1 +msgid "Default Printer" +msgstr "Máy in mặc định" + +#: lib/confg.c:388 lib/options.c:703 +#, c-format +msgid "invalid variable identifier `%s'" +msgstr "dấu hiệu nhận diện biến không hợp lệ `%s'" + +#: lib/confg.c:417 +#, c-format +msgid "obsolete `%s' entry. Ignored" +msgstr "mục « %s » quá thời nên bị bo qua" + +#: lib/encoding.c:1115 lib/encoding.c:1136 +msgid "Known Encodings" +msgstr "Biết mã ký tự" + +#: lib/faces.c:156 +msgid "incomplete knowledge of faces" +msgstr "chưa biết đủ thông tin về mặt phông chữ" + +#: lib/filtdir.c:113 +#, c-format +msgid "cannot close directory `%s'" +msgstr "không thể đóng thư mục « %s »" + +#: lib/getnum.c:63 lib/getnum.c:121 lib/getnum.c:153 lib/getnum.c:221 +#: lib/argmatch.c:134 +#, c-format +msgid "invalid argument `%s' for `%s'" +msgstr "đối số không hợp lệ « %s » cho « %s »" + +#: lib/getnum.c:123 +#, c-format +msgid "Valid arguments are integers n such that: %s\n" +msgstr "Đối số hợp lệ là số nguyên n để mà: %s\n" + +#: lib/getnum.c:223 +#, c-format +msgid "Valid arguments are floats f such that: %s\n" +msgstr "Đối số hợp lệ là điều nổi f để mà: %s\n" + +#: lib/jobs.c:307 +#, c-format +msgid "unknown encoding `%s'" +msgstr "không biết mã ký tự « %s »" + +#: lib/madir.c:59 +msgid "rows first" +msgstr "hàng trước" + +#: lib/madir.c:62 +msgid "columns first" +msgstr "cột trước" + +#: lib/media.c:173 +#, c-format +msgid "unknown medium `%s'" +msgstr "không biệt vật vật chứa « %s »" + +#: lib/media.c:208 lib/media.c:237 +msgid "Known Media" +msgstr "Vật chứa đã biết" + +#: lib/media.c:212 +msgid "dimensions" +msgstr "kích thước" + +#. TRANS: Variables (formely called `macro meta sequences', eeeaerk) +#. are things such as #(psnup) which is substituted to a bigger strings, +#. e.g. -#v #?q|-q|| #?j|-d|| #?r||-c| -w#w -h#h +#: lib/metaseq.c:104 lib/metaseq.c:113 +msgid "Known Variables" +msgstr "Biết biến" + +#: lib/metaseq.c:288 lib/metaseq.c:300 +#, c-format +msgid "Printed by %s" +msgstr "In do %s" + +#: lib/metaseq.c:298 +#, c-format +msgid "Printed by %s from %s" +msgstr "In do %s từ %s" + +#: lib/metaseq.c:308 lib/metaseq.c:327 +msgid "cannot get current working directory" +msgstr "không thể lấy thư mục hoặt động hiện có" + +#: lib/metaseq.c:954 lib/metaseq.c:987 +#, c-format +msgid "%s: too long argument for %s escape" +msgstr "%s: đối số quá dài cho dãy thoát %s" + +#. Translators: please make a short date format +#. * according to the std form in your language, using +#. * the standard strftime(3) +#: lib/metaseq.c:364 lib/metaseq.c:675 +msgid "%b %d, %y" +msgstr "%d/%b/%y" + +#. Translators: please make a long date format +#. * according to the std form in your language, using +#. * GNU strftime(3) +#: lib/metaseq.c:373 lib/metaseq.c:684 +msgid "%A %B %d, %Y" +msgstr "%A, ngày %e, %B, năm %Y" + +#: lib/metaseq.c:1234 lib/metaseq.c:1246 lib/metaseq.c:1289 +#, c-format +msgid "%s: unknown `%s' escape `%c' (%d)" +msgstr "%s: không biết « %s » dãy thoát « %c » (%d)" + +#, c-format +msgid "Page %d" +msgstr "Trang %d" + +#. `%Q' localized `Page %d/%c' +#: lib/metaseq.c:431 lib/metaseq.c:826 +#, c-format +msgid "Page %d/%c" +msgstr "Trang %d/%c" + +#: lib/metaseq.c:972 +#, c-format +msgid "%s: missing `%c' for %s%c escape" +msgstr "%s: thiếu « %c »cho dãy thoát %s%c " + +#: lib/metaseq.c:593 lib/metaseq.c:1021 +#, c-format +msgid "%s: invalid separator `%s%c' for `%s' escape" +msgstr "%s: dấu ngân cách không hợp lệ « %s%c » cho dãy thoát « %s »." + +#: lib/metaseq.c:601 lib/metaseq.c:611 +#, c-format +msgid "%s: invalid argument for %s%c escape" +msgstr "%s: đối số không hợp lệ cho dãy thoát « %s%c »." + +#: lib/metaseq.c:822 +#, c-format +msgid "Page %d/%d" +msgstr "Trang %d/%d" + +#: lib/metaseq.c:1266 +msgid "output command" +msgstr "lệnh xuất" + +#: makeinfo/makeinfo.c:340 +#, c-format +msgid "Try `%s --help' for more information.\n" +msgstr "Hãy thử lệnh « %s --help » để tìm thông tin thêm (_trợ giúp_).\n" + +#: lib/output.c:466 +#, c-format +msgid "invalid face `%s'" +msgstr "mặt phông chữ không hợp lệ « %s »" + +#: lib/output.c:538 +#, c-format +msgid "`%s' with no matching `%s'" +msgstr "« %s » không có « %s » khớp với nhau" + +#: lib/ppd.c:108 +msgid "Known Fonts" +msgstr "Biết phông chữ" + +#. TRANS: This `none' is an answer to `List of known fonts: None' +#: lib/ppd.c:111 +msgid "" +"\n" +" None.\n" +msgstr "" +"\n" +" Không có.\n" + +#: lib/ppd.c:149 lib/ppd.c:165 +msgid "Known PostScript Printer Descriptions" +msgstr "Biết mô tả máy in PostScript" + +#: lib/prange.c:305 lib/prange.c:323 +#, c-format +msgid "invalid interval `%s'" +msgstr "khoảng không hợp lệ « %s »" + +#: lib/printers.c:396 lib/printers.c:413 +#, c-format +msgid "no command for the `%s' (%s%s)" +msgstr "không có lệnh cho « %s » (%s%s)" + +#: lib/printers.c:429 +msgid "sent to the standard output" +msgstr "đã gởi cho thiết bị xuất chuẩn" + +#: lib/printers.c:430 +msgid "sent to the default printer" +msgstr "đã gởi cho máy in mặc định" + +#: lib/printers.c:435 +#, c-format +msgid "saved into the file `%s'" +msgstr "đã lưu vào tập tin « %s »" + +#: lib/printers.c:436 +#, c-format +msgid "sent to the printer `%s'" +msgstr "đã gởi cho máy in « %s »" + +#: lib/printers.c:613 lib/printers.c:621 +msgid "Known Outputs (Printers, etc.)" +msgstr "Thiết bị xuất đã biết (máy in v.v.)" + +#: lib/prolog.c:98 lib/prolog.c:181 +msgid "Known Prologues" +msgstr "Biết đoạn mở đầu" + +#: lib/prolog.c:579 +#, c-format +msgid "font %f too big" +msgstr "phông chữ %f quá lớn" + +#: lib/psgen.c:662 +#, c-format +msgid "`%s' is a binary file, printing aborted" +msgstr "`%s' là tập tin nhị phân nên thôi in" + +#: lib/quotearg.c:259 lib/quotearg.c:245 +msgid "`" +msgstr "« " + +#: lib/quotearg.c:203 lib/quotearg.c:246 gnulib/lib/quotearg.c:241 +#: lib/quotearg.c:260 +msgid "'" +msgstr " »" + +#: lib/userdata.c:129 +msgid "user" +msgstr "người dùng" + +#: lib/userdata.c:130 ../calendar/libecal/e-cal.c:5030 +#: ../servers/groupwise/e-gw-connection.c:168 +msgid "Unknown User" +msgstr "Không biết người dùng" + +#: lib/useropt.c:75 +#, c-format +msgid "unknown user option `%s'" +msgstr "không biết tùy chọn cho người dùng « %s »" + +#: lib/useropt.c:86 lib/useropt.c:97 +msgid "Known User Options" +msgstr "Tùy chọn cho người dùng đã biết" + +#: compat/regex.c:1008 lib/regcomp.c:167 +msgid "Memory exhausted" +msgstr "Hết bộ nhớ hoàn toàn" + +#: lib/xbackupfile.c:240 lib/xbackupfile.c:252 +#, c-format +msgid "cannot rename file `%s' as `%s'" +msgstr "không thể thay đổi tên tập tin « %s » thành « %s »" + +#: lib/xbackupfile.c:255 +#, c-format +msgid "restored file `%s'" +msgstr "đã phục hồi tập tin « %s »" + +#: lib/argmatch.c:159 +#, c-format +msgid "invalid argument %s for `%s'" +msgstr "đối số không hợp lệ %s cho « %s »" + +#: lib/argmatch.c:160 +#, c-format +msgid "ambiguous argument %s for `%s'" +msgstr "đối số mơ hồ %s cho « %s »" + +#: gnulib/lib/argmatch.c:157 lib/argmatch.c:155 lib/argmatch.c:157 +#, c-format +msgid "Valid arguments are:" +msgstr "Các đối số hợp lệ:" + +#: src/ant-phone.c:172 +#, c-format +msgid "" +"Usage: %s [OPTION...]\n" +"\n" +"Options:\n" +" -h, --help Show this help message\n" +" -v, --version Print version information\n" +" -r, --cleanup Remove stale socket file (left by accident by\n" +" previous run)\n" +" -d, --debug[=level] Print additional runtime debugging data to stdout\n" +" level = 1..2\n" +" -i, --soundin=DEVICE OSS compatible device for input (recording),\n" +" default: /dev/dsp\n" +" -o, --soundout=DEVICE OSS compatible device for output (playback),\n" +" default: /dev/dsp\n" +" -m, --msn=MSN identifying MSN (for outgoing calls), 0 for " +"master\n" +" MSN of this termination/port\n" +" default: 0\n" +" -l, --msns=MSNS MSNs to listen on, semicolon-separated list or " +"'*'\n" +" default: *\n" +" -c, --call=NUMBER Call specified number\n" +"\n" +"Note: If arguments of --soundin and --soundout are equal, a full duplex\n" +" sound device is needed.\n" +msgstr "" +"Cách sử dụng: %s [TÙY_CHỌN...]\n" +"\n" +"Tùy chọn:\n" +" -h, --help Hiển thị thông điệp _trợ giúp_ này\n" +" -v, --version Hiển thị thông tin _phiên bản_\n" +" -r, --cleanup Bỏ tập tin ổ cắm cũ (còn lại bất ngờ sau chạy " +"trước) (_xoá_) -d, --debug[=MỨC] In dữ liệu _gỡ lỗi_ thời chạy thêm xuất " +"thiết bị xuất chuẩn (stdout)\n" +" MỨC = 1..2\n" +" -i, --soundin=THIIẾT_BỊ thiết bị gõ tương thích với phần mềm nguồn mở " +"(ghi)\n" +"\t\t\t\t(_âm thành vào_) mặc định: /dev/dsp\n" +" -o, --soundout=THIẾT_BỊ thiết bị xuất tương thích với phần mềm nguồn mở " +"(phát)\n" +"\t\t\t\t(_âm thành ra_) mặc định: /dev/dsp\n" +" -m, --msn=SỐ số đa người ký tên (Multiple Subscriber Number: MSN) " +"nhận biết để gọi qua điện thoại,\n" +"\t\t\t0 cho MSN chính của cổng/thiết bị cuối này, mặc định: 0\n" +" -l, --msns=NHỮNG_SỐ Những MSN để nghe qua, danh sách phân cách bằng dấu " +"phẩy\n" +"\t\t\thay dấu sao '*', mặc định: *\n" +" -c, --call=SỐ _gọi_ số điện thoại đó\n" +"\n" +"Ghi chú : nếu hai đối số --soundin (âm thành vào) và --soundout (âm thành " +"xuất\n" +"là bằng nhau thì cần thiết bị âm thành truyền dẫn hai chiếu đầy đủ (full " +"duplex).\n" + +#: src/ant-phone.c:223 +#, c-format +msgid "Calling %s... " +msgstr "Gọi %s..." + +#: src/ant-phone.c:227 +msgid "successful.\n" +msgstr "thành công.\n" + +#: src/callerid.c:131 +msgid "Delete Entry" +msgstr "Xoá bỏ mục" + +#: src/callerid.c:143 +msgid "" +"Are you sure you want to\n" +"delete this entry?" +msgstr "" +"Bạn có chắc muốn xoá bỏ\n" +"mục này không?" + +#: src/callerid.c:148 +msgid "Delete recording" +msgstr "Xoá bỏ mục ghi" + +#: src/callerid.c:266 +#, c-format +msgid "Enter the base filename for %s file" +msgstr "Hãy nhập tên tập tin cơ bản cho tập tin %s" + +#: src/callerid.c:312 +msgid "Do you really want to delete this recording?" +msgstr "Bạn chắc muốn xoá bỏ mục ghi này không?" + +#: src/callerid.c:336 src/callerid.c:362 +msgid "/_Playback" +msgstr "/_Phát lại" + +#: src/callerid.c:337 src/callerid.c:366 +msgid "/_Save as..." +msgstr "/_Lưu là..." + +#: src/callerid.c:338 src/callerid.c:370 +msgid "/Delete _Recording" +msgstr "/Xoá bỏ mục _ghi" + +#: src/callerid.c:340 src/callerid.c:374 +msgid "/_Delete Row" +msgstr "/Xoá bỏ _hàng" + +#: src/callerid.c:423 src/gtksettings.c:455 +msgid "Caller ID" +msgstr "Xem số người gọi" + +#: ../src/sj-main.c:1245 ../src/source-view.c:211 +#: ../mimedir/mimedir-vcomponent.c:340 +#, fuzzy +msgid "Duration" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Thời gian\n" +"#-#-#-#-# libmimedir.vi.po (libmimedir HEADnReport-Msgid-Bugs-To: ) #-#-#-" +"#-#\n" +"Thời lượng" + +#: src/callerid.c:748 +msgid "(UNKNOWN)" +msgstr "• Không biết •" + +#: src/callerid.c:818 +#, c-format +msgid "ANT: %d unanswered" +msgstr "ANT: %d chưa trả lời" + +#: src/controlpad.c:101 +#, c-format +msgid "Preset %c" +msgstr "Đặt trước %c" + +#: src/controlpad.c:108 +#, c-format +msgid "Please input new preset data for button %c:" +msgstr "Hãy nhập dữ liệu đặt trước mới cho cái nút %c:" + +#: src/controlpad.c:124 src/gtk.c:553 +msgid "Number:" +msgstr "Số đt:" + +#: ../gcalctool/calctool.c:196 +msgid "Backspace" +msgstr "Xoá lùi" + +#: src/controlpad.c:315 +msgid "Preset 1" +msgstr "Lập trước 1" + +#: src/controlpad.c:316 +msgid "Clear Number" +msgstr "Xoá số" + +#: src/controlpad.c:316 +msgid "Preset 2" +msgstr "Lập trước 2" + +#: src/controlpad.c:317 +msgid "Redial" +msgstr "Quay số lại" + +#: src/controlpad.c:317 +msgid "Preset 3" +msgstr "Lập trước 3" + +#: src/controlpad.c:318 +msgid "Mute Microphone" +msgstr "Tắt máy vi âm" + +#: src/controlpad.c:318 +msgid "Preset 4" +msgstr "Lập trước 4" + +#: ../objects/FS/function.c:1178 ../objects/UML/classicon.c:127 +msgid "Control" +msgstr "Điều khiển" + +#: src/controlpad.c:423 +msgid "Recording" +msgstr "Ghi" + +#: src/controlpad.c:434 +msgid "Record to file" +msgstr "Ghi vào tập tin" + +#: src/controlpad.c:445 +msgid "Record local channel" +msgstr "Ghi kênh địa phương" + +#: src/controlpad.c:456 +msgid "Record remote channel" +msgstr "Ghi kênh từ xa" + +#: src/gtk.c:222 src/gtksettings.c:229 +msgid "ANT Note" +msgstr "Ant: chú thích" + +#: src/gtk.c:223 +msgid "" +"Can't open audio device.\n" +"Please stop other applications using\n" +"the audio device(s) or check your\n" +"device settings and try again." +msgstr "" +"Không thể mở thiết bị âm thanh.\n" +"Hãy thôi các thiết bị khác sử dụng\n" +"cùng thiết bị âm thanh đó hay kiểm tra\n" +"thiết lập thiết bị và thử lại." + +#: src/gtk.c:252 +msgid "Sound input device:" +msgstr "Thiết bị gõ âm thanh:" + +#: src/gtk.c:253 +msgid "Input speed:" +msgstr "Tốc độ gõ :" + +#: src/gtk.c:253 src/gtk.c:256 src/gtk.c:257 src/gtk.c:259 src/gtk.c:263 +#: src/gtk.c:266 src/gtk.c:267 src/gtk.c:269 +msgid "[inactive]" +msgstr "[không làm gì]" + +#: src/gtk.c:255 +msgid "Input sample size (bits):" +msgstr "Cỡ mẫu gõ (theo bit):" + +#: src/gtk.c:257 src/gtk.c:267 +msgid "Input fragment size (samples):" +msgstr "Cỡ mảnh gõ (theo mẫu):" + +#: src/gtk.c:259 +msgid "Input channels:" +msgstr "Kênh gõ :" + +#: src/gtk.c:262 +msgid "Sound output device:" +msgstr "Thiết bị âm thanh xuất:" + +#: src/gtk.c:263 +msgid "Output speed:" +msgstr "Tốc độ xuất:" + +#: src/gtk.c:265 +msgid "Output sample size (bits):" +msgstr "Cỡ mẫu xuât (theo bit):" + +#: src/gtk.c:269 +msgid "Output channels:" +msgstr "Kênh xuất:" + +#: src/gtk.c:272 +msgid "ISDN device:" +msgstr "Thiết bị ISDN:" + +#: src/gtk.c:273 +msgid "ISDN speed (samples):" +msgstr "Tốc độ ISDN (theo mẫu):" + +#: src/gtk.c:274 +msgid "ISDN sample size (bits):" +msgstr "Cỡ mẫu ISDN (theo bit):" + +#: src/gtk.c:275 +msgid "ISDN fragment size (bytes):" +msgstr "Cỡ mảnh ISDN (theo byte):" + +#: src/gtk.c:287 +msgid "ANT Info" +msgstr "Thông tin ANT" + +#: src/gtk.c:358 +msgid "About ANT" +msgstr "Giới thiệu ANT" + +#: src/gtk.c:374 +#, c-format +msgid "" +"ANT (ANT is Not a Telephone) Version %s\n" +"Copyright 2002, 2003 Roland Stigge\n" +"\n" +"This is an ISDN telephone application\n" +"written for GNU/Linux and ISDN4Linux for\n" +"communicating via a full duplex soundcard (or\n" +"multiple sound devices if you like) and an\n" +"audio capable ISDN4Linux ISDN device\n" +"\n" +"Contact:\n" +"Roland Stigge, stigge@antcom.de\n" +"http://www.antcom.de/\n" +"Mailing list: ant-phone-devel@nongnu.org" +msgstr "" +"ANT (ANT is Not a Telephone) phiên bản %s\n" +"(ANT không phải là một máy điện thoại)Bản quyền © năm 2002, 2003 Roland " +"Stigge\n" +"\n" +"Đây là một ứng dụng điện thoai ISDN\n" +"được tạo cho GNU/Linux và ISDN4Linux\n" +"để truyền thông qua thẻ âm thanh truyền dẫn\n" +"hai chiếu đầy đủ (hay số nhiều thiết bị nếu muốn)\n" +"và thiết bị ISDN ISDN4Linux có thể gởi âm thanh.\n" +"\n" +"Liên lạc:\n" +"Roland Stigge, stigge@antcom.de\n" +"http://www.antcom.de/\n" +"Hộp thư chung: ant-phone-devel@nongnu.org" + +#: src/gtk.c:412 +msgid "ANT License" +msgstr "Quyền ANT" + +#: src/gtk.c:413 +msgid "" +"ANT (ANT is Not a Telephone)\n" +"Copyright (C) 2002, 2003 Roland Stigge\n" +"\n" +"This program is free software; you can redistribute it and/or\n" +"modify it under the terms of the GNU General Public License\n" +"as published by the Free Software Foundation; either version 2\n" +"of the License, or (at your option) any later version.\n" +"\n" +"This program is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with this program; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA." +msgstr "" +"ANT (ANT is Not a Telephone)\n" +"Bản quyền © năm 2002, 2003 Roland Stigge\n" +"\n" +"Chương trình này là phần mềm tự do; bạn có thể phân phối nó lại và/hay\n" +"sửa đổi nó theo điều kiện của Quyền Công Chung Gnu (GPL)\n" +"như xuất do Tổ chức Phần mềm Tự do (Free Software Foundation)\n" +"hoặc phiên bản 2 của quyền đó hoặc (tùy chọn) bất cứ phiên bản sau nào.\n" +"\n" +"Chúng tôi phân phối chương trình này vì mong nó hữu ích,\n" +"nhưng nó không bảo đảm gì cả, không có bảo đảm ngụ ý ngay cả\n" +"khả năng bán hay khả năng làm việc dứt khoát.\n" +"Hãy xem Quyền Công Chung Gnu (GPL) để tìm chi tiết.\n" +"\n" +"Nếu bạn chưa nhận một bản Quyền Công Chung Gnu\n" +"(Gnu General Public Licence) cùng với chương trinh này thì hãy\n" +"viết cho Tổ chức Phần mềm Tự do:\n" +"Free SoftwareFoundation, Inc.,\n" +"59 Temple Place - Suite 330,\n" +"Boston, MA 02111-1307, USA (Mỹ)." + +#: src/gtk.c:446 +msgid "/Phon_e" +msgstr "/_Điện thoại" + +#: src/gtk.c:447 +msgid "/Phone/_Info Window" +msgstr "/Điện thoại/Cửa sổ thông t_in" + +#: src/gtk.c:448 src/gtk.c:496 +msgid "/Phone/_Line Level Check" +msgstr "/Điện thoại/Kiểm tra mức _dòng" + +#: src/gtk.c:450 +msgid "/Phone/" +msgstr "/Điện thoại/" + +#: src/gtk.c:451 +msgid "/Phone/_Quit" +msgstr "/Điện thoại/_Thoát" + +#: src/gtk.c:453 ../app/menus.c:141 ../app/menus.c:142 ../pan/gui.c:1659 +#: jpilot.c:1396 +msgid "/_View" +msgstr "/_Xem" + +#: src/gtk.c:454 src/gtk.c:484 +msgid "/View/_Caller ID Monitor" +msgstr "/Xem/Theo dõi số người dùng" + +#: src/gtk.c:456 src/gtk.c:487 +msgid "/View/_Line Level Meters" +msgstr "/Xem/Đo mức _dòng" + +#: src/gtk.c:458 src/gtk.c:490 +msgid "/View/Control _Pad" +msgstr "/Xem/_Bảng điều khiển" + +#: src/gtk.c:460 src/metro.c:978 +msgid "/_Options" +msgstr "/Tùy _chọn" + +#: src/gtk.c:461 src/gtk.c:493 +msgid "/Options/_Settings" +msgstr "/Tùy chọn/Thiết _lập" + +#: ../app/menus.c:73 ../app/menus.c:263 ../pan/gui.c:1782 jpilot.c:1437 +msgid "/_Help" +msgstr "/Trợ _giúp" + +#: src/gtk.c:464 src/metro.c:993 +msgid "/Help/_About" +msgstr "/Trợ giúp/_Giới thiệu" + +#: src/gtk.c:465 src/metro.c:995 +msgid "/Help/_License" +msgstr "/Trợ giúp/_Quyền" + +#: src/gtk.c:543 src/gtksettings.c:431 src/session.c:68 +msgid "Dialing" +msgstr "Đang quay số..." + +#: src/gtk.c:759 src/gtk.c:760 +msgid "MUTED" +msgstr "• Câm •" + +#: src/gtksettings.c:230 +msgid "Bad isdn/sound device settings, please try again." +msgstr "Có thiết lập thiết bị ISDN/âm thanh sai nên hay thử lại." + +#: src/gtksettings.c:293 +msgid "ANT Settings" +msgstr "Thiết lập ANT" + +#: ../src/dlg-pick-applications.c:355 ../src/session.c:630 +#: ../src/resource-tree-treedata.cc:72 ../src/orca/rolenames.py:513 +msgid "Application" +msgstr "Ứng dụng" + +#: src/gtksettings.c:320 +msgid "Save options on exit" +msgstr "Lưu tùy chọn khi thoát" + +#: src/gtksettings.c:327 +msgid "Popup main window on incoming call" +msgstr "Bật lên cửa sổ chính khi nhận sự gọi" + +#: src/gtksettings.c:333 +msgid "Execute on incoming call:" +msgstr "Thi hành khi nhận cuộc gọi" + +#: src/gtksettings.c:344 +msgid "Recording Format" +msgstr "Dạng ghi" + +#: src/gtksettings.c:354 +msgid "Microsoft WAV, uLaw" +msgstr "Microsoft WAV, uLaw" + +#: src/gtksettings.c:364 +msgid "Microsoft WAV, 16-bit signed" +msgstr "Microsoft WAV, 16-bit đã ký tên" + +#: src/gtksettings.c:374 +msgid "Apple/SGI AIFF, uLaw" +msgstr "Apple/SGI AIFF, uLaw" + +#: src/gtksettings.c:384 +msgid "Apple/SGI AIFF, 16-bit signed" +msgstr "Apple/SGI AIFF, 16-bit đã ký tên" + +#: src/gtksettings.c:399 ../addressbook/libebook/e-contact.c:136 +#: ../mimedir/mimedir-vcard-phone.c:220 ../mimedir/mimedir-vcard-phone.c:756 +msgid "ISDN" +msgstr "ISDN" + +#: src/gtksettings.c:411 +msgid "Identifying MSN:" +msgstr "Số đa người ký tên (Multiplê Subscriber Number: MSN) nhân biết:" + +#: src/gtksettings.c:421 +msgid "Listen to MSNs:" +msgstr "Nghe qua những số đa người ký tên (MSN) này:" + +#: src/gtksettings.c:443 +msgid "Dial history size:" +msgstr "Cỡ lịch sử quay số :" + +#: src/gtksettings.c:467 +msgid "Maximum CID rows:" +msgstr "Tối đa hàng số người gởi:" + +#: src/gtksettings.c:475 src/gtksettings.c:503 +msgid "[no limit]" +msgstr "[vô vùng] " + +#: src/gtksettings.c:484 +msgid "Read isdnlog data on startup" +msgstr "Đọc dữ liệu isdnlog (bản ghi ISDN) khi khởi động" + +#: src/gtksettings.c:491 +msgid "Maximum days to read from isdnlog:" +msgstr "Đọc từ isdnlog (bản ghi ISDN) được số ngày (tối đa):" + +#: src/gtksettings.c:520 +msgid "Sound Devices" +msgstr "Thiết bị âm thanh" + +#: src/gtksettings.c:522 +msgid "OSS" +msgstr "Phần mềm nguồn mở tự do" + +#: src/gtksettings.c:534 +msgid "Input sound device:" +msgstr "Thiết bị âm thanh gõ :" + +#: src/gtksettings.c:546 +msgid "Output sound device:" +msgstr "Thiết bị âm thanh xuất:" + +#: src/gtksettings.c:559 +msgid "Release unused devices" +msgstr "Nhả các thiết bị chưa sử dụng lại" + +#: src/llcheck.c:371 +msgid "Line Level Check" +msgstr "Kiểm tra mức dòng" + +#: src/llcheck.c:383 +msgid "" +"Please check the line input level\n" +"and adjust it using your favorite\n" +"mixer application.\n" +"You can also play a sound\n" +"to test the sound output." +msgstr "" +"Hãy kiểm tra mức gõ dòng\n" +"và điều chỉnh nó bằng ứng dụng\n" +"hoà tiếng ưa thích của bạn.\n" +"Bạn cũng có thể phát tiếng\n" +"để thử xuất âm thanh ra." + +#: src/llcheck.c:405 ../grecord/src/gsr-window.c:1737 +msgid "Play sound" +msgstr "Phát âm" + +#: ../src/bb_util.c:279 +msgid "Ready" +msgstr "Sẵn sàng" + +#: src/session.c:65 ../srcore/srpres.c:876 address_gui.c:1929 +#: address_gui.c:1932 address_gui.c:2941 dialer.c:308 dialer.c:333 +#: ../src/orca/rolenames.py:223 +#, fuzzy +msgid "Dial" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Quay số\n" +"#-#-#-#-# jpilot-0.99.8-pre12.vi.po (jpilot-0.99.8-pre12) #-#-#-#-#\n" +"Quay số\n" +"#-#-#-#-# orca.vi.po (orca HEAD) #-#-#-#-#\n" +"Thoại" + +#: src/session.c:65 src/session.c:69 src/session.c:70 +msgid "Hang up" +msgstr "Ngừng nói" + +#: src/session.c:66 src/session.c:67 +msgid "RING" +msgstr "REO" + +#: src/session.c:66 src/session.c:67 +msgid "Answer" +msgstr "Trả lời" + +#: src/session.c:66 src/session.c:67 +msgid "Reject" +msgstr "Loại ra" + +#: src/session.c:68 src/session.c:69 src/session.c:70 src/session.c:71 +msgid "Pick up" +msgstr "Lấy" + +#: src/session.c:69 +msgid "B-Channel open" +msgstr "Kênh-B mở" + +#: src/session.c:70 ../configure.c:463 ../gnometris/tetris.cpp:777 +msgid "Setup" +msgstr "Thiết lập" + +#: src/session.c:71 ../gst-mixer/src/element.c:230 +#: ext/alsa/gstalsamixertrack.c:92 +msgid "Playback" +msgstr "Phát lại" + +#: src/session.c:377 +#, c-format +msgid "Preset %d" +msgstr "Đặt trước %d" + +#: src/session.c:699 +msgid "(HW ERROR)" +msgstr "(• Lỗi phần cứng •)" + +#: src/session.c:986 +msgid "Audio OFF" +msgstr "TẮT âm thanh" + +#: src/session.c:986 +msgid "Audio ON" +msgstr "MỞ âm thanh" + +#: src/session.c:1081 +msgid "(BUSY)" +msgstr "(• Bận •)" + +#: src/session.c:1087 +msgid "(TIMEOUT)" +msgstr "(• Hết thời •)" + +#: src/session.c:1121 +msgid "(RUNG)" +msgstr "(• Đã rung •)" + +#: src/session.c:1361 +msgid "(ABORTED)" +msgstr "(• Bị hủy bỏ •)" + +#: src/session.c:1369 +msgid "(REJECTED)" +msgstr "(• Bị từ chối •)" + +#: src/authmode.c:469 +msgid "Entering XDB loop..." +msgstr "Vào vòng lặp XDB..." + +#: src/authmode.c:481 src/authmode.c:503 +msgid "Exiting XDB loop..." +msgstr "Xuất vòng lặp XDB..." + +#: src/authmode.c:526 +msgid "Database not specified" +msgstr "Chưa ghi rõ cơ sở dữ liệu" + +#: src/authmode.c:534 +#, c-format +msgid "Cannot open database %s: %s" +msgstr "Không thể mở cơ sở dữ liệu %s: %s" + +#: src/authmode.c:541 +#, c-format +msgid "Found record for `%s'." +msgstr "Tìm thấy mục ghi cho « %s »." + +#: src/authmode.c:546 +#, c-format +msgid "Cannot retrieve data from the SASL database: %s" +msgstr "Không thể lấy dữ liệu từ cơ sở dữ liêu SASL: %s" + +#: src/authmode.c:551 +#, c-format +msgid "Record for `%s' not found." +msgstr "Không tìm thấy mục ghi cho « %s »." + +#: src/authmode.c:606 +msgid "MTA has not been specified. Set either REMOTE-MTA or LOCAL-MTA." +msgstr "" +"Chưa ghi rõ MTA (tác nhân chuyển giao thông điệp). Hãy lập hoặc REMOTE-MTA " +"(MTA ở xa) hay LOCAL-MTA (MTA cục bộ)." + +#: src/authmode.c:637 src/map.c:164 src/net.c:150 src/net.c:217 +#: src/transmode.c:98 +#, c-format +msgid "Illegal address length received for host %s" +msgstr "Nhận độ dài địa chỉ sai cho máy %s" + +#: src/authmode.c:651 src/transmode.c:112 +msgid "Loop not allowed. Connection rejected." +msgstr "Không cho phép vòng lặp nên từ chối kết nối." + +#: src/authmode.c:683 src/transmode.c:143 +msgid "Connection closed successfully." +msgstr "Đã đóng kết nối." + +#: src/authmode.c:690 src/transmode.c:149 +msgid "PAM: Session closed." +msgstr "PAM: đã đóng phiên chạy." + +#: src/authmode.c:694 src/transmode.c:153 +msgid "PAM: failed to release authenticator." +msgstr "PAM: không nhả điều xác thực được" + +#: src/daemon.c:49 +msgid "daemon() failed" +msgstr "daemon() (tập lệnh trình nền) không thành công" + +#: src/daemon.c:56 +msgid "Cannot fork." +msgstr "Không thể tạo tiến trình con." + +#: src/daemon.c:64 +msgid "setsid() failed" +msgstr "setsid() không thành công" + +#: src/daemon.c:76 +#, c-format +msgid "%s daemon startup succeeded." +msgstr "%s khởi động trình nền được." + +#: src/daemon.c:88 +#, c-format +msgid "Exited successfully" +msgstr "Đã thoát thành công" + +#: src/daemon.c:90 +#, c-format +msgid "Failed with status %d" +msgstr "Không thành công với trạng thái %d" + +#: src/daemon.c:95 +#, c-format +msgid "Terminated on signal %d" +msgstr "Bị kết thúc tại tín hiệu %d" + +#: src/daemon.c:98 +#, c-format +msgid "Stopped on signal %d" +msgstr "Ngừng tại tín hiệu %d" + +#: src/daemon.c:101 +#, c-format +msgid "Dumped core" +msgstr "Lõi bị đổi" + +#: src/daemon.c:104 signame.c:142 +#, c-format +msgid "Terminated" +msgstr "Bị kết thúc" + +#: src/daemon.c:121 +#, c-format +msgid "Child [%lu] finished. %s. %d client left." +msgid_plural "Child [%lu] finished. %s. %d clients left." +msgstr[0] "Tiến trình con [%lu] đã xong. %s. %d trình/máy khách còn lại" + +#: src/daemon.c:169 +msgid "WARNING: An unprivileged user has not been specified!" +msgstr "CẢNH BÁO : chưa ghi rõ người dùng không co quyền." + +#: src/daemon.c:214 +msgid "GNU Anubis is running..." +msgstr "Trình Anubis của GNU đang chạy..." + +#: src/daemon.c:226 src/exec.c:122 +msgid "accept() failed" +msgstr "accept() không thành công" + +#: src/daemon.c:244 +#, c-format +msgid "TCP wrappers: connection from %s:%u rejected." +msgstr "Lớp bọc choTCP: kết nối từ %s:%u bị từ chối." + +#: src/daemon.c:269 +#, c-format +msgid "Too many clients. Connection from %s:%u rejected." +msgstr "Quá nhiều máy khách: kết nối từ %s:%u bị từ chối." + +#: src/daemon.c:276 +#, c-format +msgid "Connection from %s:%u" +msgstr "Kết nối từ %s:%u" + +#: src/daemon.c:281 +msgid "daemon: cannot fork" +msgstr "trình nền: không thể tạo tiến trình con" + +#: src/daemon.c:362 src/transmode.c:68 +msgid "The MTA has not been specified. Set the REMOTE-MTA or LOCAL-MTA." +msgstr "" +"Chưa ghi rõ MTA (tác nhân chuyển giao thông điệp). Hãy lập hoặc REMOTE-MTA " +"(MTA ở xa) hay LOCAL-MTA (MTA cục bộ)." + +#: src/env.c:172 +#, c-format +msgid "Try '%s --help' for more information." +msgstr "Hãy thử lệnh '%s --help' (trơ giúp) để xem thông tin thêm." + +#: src/env.c:273 +#, c-format +msgid "setgroups(1, %lu) failed" +msgstr "setgroups(1, %lu) không thành công" + +#: src/env.c:283 +#, c-format +msgid "setegid(%lu) failed" +msgstr "setegid(%lu) không thành công" + +#: src/env.c:286 +#, c-format +msgid "setregid(%lu,%lu) failed" +msgstr "setregid(%lu,%lu) không thành công" + +#: src/env.c:290 +#, c-format +msgid "setresgid(%lu,%lu,%lu) failed" +msgstr "setresgid(%lu,%lu,%lu) không thành công" + +#: src/env.c:299 +#, c-format +msgid "setgid(%lu) failed" +msgstr "setgid(%lu) không thành công" + +#: src/env.c:302 +#, c-format +msgid "cannot set effective gid to %lu" +msgstr "không thể lập GID hữu ích cho %lu" + +#: src/env.c:324 +#, c-format +msgid "setreuid(%lu,-1) failed" +msgstr "setreuid(%lu,-1) không thành công" + +#: src/env.c:330 +#, c-format +msgid "second setuid(%lu) failed" +msgstr "second setuid(%lu) không thành công" + +#: src/env.c:338 +#, c-format +msgid "setuid(%lu) failed" +msgstr "setuid(%lu) không thành công" + +#: src/env.c:347 +msgid "seteuid(0) succeeded when it should not" +msgstr "seteuid(0) thành công khi không nên." + +#: src/env.c:352 +msgid "cannot drop non-root setuid privileges" +msgstr "không bỏ được quyền truy cập setuid không phải của người chủ" + +#: src/env.c:379 +msgid "PAM: Session opened (restrictions applied)." +msgstr "PAM: đã mở phiên chạy (đã áp dụng các hạn chế)." + +#: src/env.c:382 +msgid "PAM: Not authenticated to use GNU Anubis." +msgstr "PAM: không có xác thực để sử dụng trình Anubis của GNU." + +#: src/env.c:394 src/main.c:156 +#, c-format +msgid "UID:%d (%s), GID:%d, EUID:%d, EGID:%d" +msgstr "UID:%d (%s), GID:%d, EUID:%d, EGID:%d" + +#: src/env.c:429 +#, c-format +msgid "Invalid user ID: %s" +msgstr "UID (thông tin nhận biết người dùng) không hợp lệ: %s" + +#: src/env.c:435 +#, c-format +msgid "Invalid user name: %s" +msgstr "Tên người dùng không hợp lệ: %s" + +#: src/env.c:458 +#, c-format +msgid "Wrong permissions on %s. Set 0600." +msgstr "Quyền truy cập sai với %s. Lập 0600." + +#: src/env.c:484 +#, c-format +msgid "%s is not a regular file or a symbolic link." +msgstr "%s không phải là một tập tin bình thường hay một liên kết mềm." + +#: src/env.c:507 +#, c-format +msgid "Unknown mode: %s" +msgstr "Không biết chế độ : %s" + +#: src/env.c:522 +#, c-format +msgid "Cannot open pid file '%s'" +msgstr "Không thể mở tập tin PID '%s'" + +#: src/errs.c:96 +#, c-format +msgid "Could not write to socket: %s" +msgstr "Không thể ghi vào ổ cắm: %s" + +#: src/errs.c:98 +msgid "Could not write to socket" +msgstr "Không thể ghi vào ổ cắm" + +#: src/errs.c:108 +#, c-format +msgid "Unknown host %s." +msgstr "Không biết máy %s." + +#: src/errs.c:111 +#, c-format +msgid "%s: host name is valid but does not have an IP address." +msgstr "%s: tên máy là hợp lệ nhưng mà không có địa chỉ IP." + +#: src/errs.c:115 +#, c-format +msgid "%s: unrecoverable name server error occurred." +msgstr "%s: gặp lỗi máy phục vụ tên không thể phục hồi." + +#: src/errs.c:118 +#, c-format +msgid "%s: a temporary name server error occurred. Try again later." +msgstr "%s: gặp lỗi máy phục vụ tên tạm thời nên hãy thư lại lần sau." + +#: src/errs.c:121 +#, c-format +msgid "%s: unknown DNS error %d." +msgstr "%s: không biết lỗi DNS %d." + +#: src/esmtp.c:165 +msgid "Malformed or unexpected reply" +msgstr "Trả lời dạng sai hay rỗng." + +#: src/esmtp.c:189 +#, c-format +msgid "SASL gsasl_client_start: %s" +msgstr "SASL gsasl_client_start: %s" + +#: src/esmtp.c:198 src/esmtp.c:216 +#, c-format +msgid "GSASL handshake aborted: %d %s" +msgstr "Việc bắt tay GSASL bị hủy bỏ : %d %s" + +#: src/esmtp.c:227 +#, c-format +msgid "GSASL error: %s" +msgstr "Lỗi GSASL: %s" + +#: src/esmtp.c:241 +#, c-format +msgid "Authentication failed: %d %s" +msgstr "Không xác thực được: %d %s" + +#: src/esmtp.c:244 +msgid "Authentication successful." +msgstr "Đã xác thực thành công." + +#: src/esmtp.c:263 +msgid "Got empty list of authentication methods" +msgstr "Đã gọi danh sách phương pháp xác thực rỗng" + +#: src/esmtp.c:288 +msgid "Server did not offer any feasible authentication mechanism" +msgstr "Máy phục vụ chưa đưa cơ chế xác thực nào có thể" + +#: src/esmtp.c:298 +msgid "INTERNAL ERROR" +msgstr "LỖI NỘI BỘ" + +#: src/esmtp.c:306 +#, c-format +msgid "" +"Selected authentication mechanism %s requires TLS encryption. Not using " +"ESMTP authentication" +msgstr "" +"Cơ chế xác thực đã chọn %s thì cần thiết mật mã TLS. Không xác thực loại " +"ESMTP." + +#: src/esmtp.c:313 +#, c-format +msgid "Selected authentication mechanism %s" +msgstr "Cơ chế xác thực đã chon %s" + +#: src/esmtp.c:319 +#, c-format +msgid "Cannot initialize libgsasl: %s" +msgstr "Không khởi chạy được libgsasl: %s" + +#: src/esmtp.c:340 +msgid "ESMTP AUTH is not supported" +msgstr "Không hỗ trợ cach xác thực (AUTH) ESMTP" + +#: src/exec.c:82 +msgid "#1 socket() failed." +msgstr "#1 socket() (ổ cắm) không thành công." + +#: src/exec.c:87 +msgid "#2 socket() failed." +msgstr "#2 socket() (ổ cắm) không thành công." + +#: src/exec.c:97 +msgid "#1 bind() failed" +msgstr "#1 bind() (đóng kết) không thành công." + +#: src/exec.c:102 +msgid "#2 bind() failed" +msgstr "#2 bind() (đóng kết) không thành công." + +#: src/exec.c:107 src/net.c:237 +msgid "listen() failed" +msgstr "listen() (nghe) không thành công." + +#: src/exec.c:112 +#, c-format +msgid "getsockname() failed: %s." +msgstr "getsockname() (gọi tên ổ cắm) không thành công: %s." + +#: src/exec.c:117 +msgid "connect() failed" +msgstr "connect() (kết nối) không thành công." + +#: src/exec.c:129 +msgid "socketpair() failed" +msgstr "socketpair() (cặp ổ cắm) không thành công." + +#: src/exec.c:143 +#, c-format +msgid "Local program [%lu] finished." +msgstr "Chương trình cục bộ [%lu] đã xong." + +#: src/exec.c:179 +#, c-format +msgid "Executing %s %s..." +msgstr "Thực hiện %s %s..." + +#: src/exec.c:187 +msgid "fork() failed" +msgstr "fork() (tạo tiến trình con) không thành công." + +#: src/exec.c:201 +msgid "execvp() failed" +msgstr "execvp() không thành công." + +# Name of a program: do not translate/ tên chương trình: đừng dịch +#: src/gpg.c:54 +#, c-format +msgid "GPGME: %s." +msgstr "GPGME: %s." + +#: src/gpg.c:100 +#, c-format +msgid "Install GPGME version %s or later." +msgstr "Cài đặt trình GPGME phiên bản %s hay sau." + +#: src/gpg.c:107 +#, c-format +msgid "GPGME: failed. %s." +msgstr "GPGME: không thành công. %s." + +#: src/gpg.c:159 src/gpg.c:407 +#, c-format +msgid "GPGME: Cannot list keys: %s" +msgstr "GPGME: không thể liệt kê các khoá: %s" + +#: src/gpg.c:288 src/gpg.c:430 +#, c-format +msgid "GPGME: Invalid recipient encountered: %s" +msgstr "GPGME: gặp người nhận không hợp lệ: %s" + +#: src/gpg.c:333 +#, c-format +msgid "GPGME: Invalid signer found: %s" +msgstr "GPGME: tìm thấy người ký tên không hợp lệ: %s" + +#: src/gpg.c:340 +msgid "GPGME: Unexpected number of signatures created" +msgstr "GPGME: đã tạo số lượng ký tên bất ngờ" + +#: src/gpg.c:347 +msgid "GPGME: Wrong type of signature created" +msgstr "GPGME: đã tạo chữ ký kiểu không đúng" + +#: src/gpg.c:352 +#, c-format +msgid "GPGME: Wrong pubkey algorithm reported: %i" +msgstr "GPGME: đã thông báo thuật toán khoá công không đúng: %i" + +#: src/gpg.c:359 +#, c-format +msgid "GPGME: Wrong hash algorithm reported: %i" +msgstr "GPGME: đã thông báo thuật toán băm không đúng: %i" + +#: src/gpg.c:366 +#, c-format +msgid "GPGME: Wrong signature class reported: %u" +msgstr "GPGME: đã thông báo loại chữ ký không đúng: %u" + +#: src/guile.c:79 +#, c-format +msgid "cannot open guile output file %s" +msgstr "không thể mở tập tin xuất cua guile %s" + +#: src/guile.c:266 +msgid "missing procedure name" +msgstr "thiếu tên thủ tuc" + +#: src/guile.c:281 +#, c-format +msgid "%s not a procedure object" +msgstr "%s không phải là môt đối tượng thủ tục" + +#: src/guile.c:323 +#, c-format +msgid "Bad car type in return from %s" +msgstr "%s đã gởi trả kiểu car sai" + +#: src/guile.c:342 +#, c-format +msgid "Bad cdr type in return from %s" +msgstr "%s đã gởi trả kiểu cdr sai" + +#: src/guile.c:345 +#, c-format +msgid "Bad return type from %s" +msgstr "Kiểu trả vệ sai từ %s" + +#: src/help.c:85 +msgid "" +"\n" +"GNU Anubis is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation; either version 2 of the License, or\n" +"(at your option) any later version." +msgstr "" +"\n" +"Trình Anubis của GNU là phần mềm tự do: bạn có thể phân phối lại nó\n" +"và/hay sửa đổi nó với điều kiện của Quyền Công Chung GNU (GPL)\n" +"như đã xuất bởi Tổ chức Phần mềm Tự do, hoặc phiên bản 2\n" +"của Quyền đó, hay (tùy chọn) bất cứ phiên bản sau nào." + +#: src/help.c:89 +msgid "" +"\n" +"GNU Anubis is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details." +msgstr "" +"\n" +"Trình Anubis của GNU được phân phối vì chúng tôi mong nó có ích,\n" +"nhưng mà không bảo đảm gì cả,\n" +"dù khả năng bán hay khả năng làm việc dứt khoát.\n" +"Hãy xem Quyền Công Chung GNU để tìm chi tiết." + +#: src/help.c:93 +msgid "" +"\n" +"You should have received a copy of the GNU General Public License\n" +"along with GNU Anubis; if not, write to the Free Software\n" +"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" +msgstr "" +"\n" +"Bạn nên đã nhận một bản sao của Quyền Công Chung GNU (GPL)\n" +"cùng với trình Anubis của GNU; nếu không thì hãy viết thư cho:\n" +"Free Software Foundation, Inc.,\n" +"59 Temple Place, Suite 330,\n" +"Boston, MA 02111-1307 USA (Mỹ)" + +#: src/help.c:96 +msgid "" +"\n" +"GNU Anubis is released under the GPL with the additional exemption that\n" +"compiling, linking, and/or using OpenSSL is allowed.\n" +msgstr "" +"\n" +"Trình Anubis của GNU được phát hành với điều kiện của GPL\n" +"và cũng với sự miễn là cho phép biên dịch, liên kết và/hay sử dụng OpenSSL.\n" + +#: src/help.c:104 +msgid "Usage: anubis [options]\n" +msgstr "Cách sử dụng: anubis [tùy_chọn]\n" + +#: src/help.c:105 +msgid "" +" -b, --bind [HOST:]PORT Specify the TCP port on which GNU Anubis " +"listens\n" +" for connections. The default HOST is " +"INADDR_ANY,\n" +" and default PORT is 24 (private mail system)." +msgstr "" +" -b, --bind [MÁY:]CỔNG Ghi rõ cổng TCP nơi trình Anubis của GNU\n" +"\t\t\t\tlắng nghe kết nối nào. Máy mặc định là INADDR_ANY,\n" +"\t\t\t\tvà cổng mặc định là 24 (hệ thống thư cá nhân)" + +#: src/help.c:108 +msgid "" +" -r, --remote-mta HOST[:PORT] Specify a remote SMTP host name or IP " +"address.\n" +" The default PORT number is 25." +msgstr "" +" -r, --remote-mta MÁY[:CỔNG] \t\tGhi rõ tên máy SMTP _ở xa_ hay địa chỉ IP.\n" +"\t\t\t\t\t\t\t\tSố cổng mặc định là 25." + +#: src/help.c:110 +msgid "" +" -l, --local-mta FILE Execute a local SMTP server, which works on\n" +" standard input and output (inetd-type " +"program).\n" +" This option excludes the '--remote-mta' option." +msgstr "" +" -l, --local-mta FILE Thực hiện trình hỗ trợ SMTP _địa phương_\n" +"\t\t\t\tmà làm việc với thiết bị gõ/xuất chuẩn (trình kiểu inetd).\n" +"\t\t\t\tTùy chọn này loạị trừ tùy chọn '--remote-mta'." + +#: src/help.c:113 +msgid " -m, --mode=MODE Select operation mode." +msgstr " -m, --mode=CHẾ_ĐỘ Chọn _chế độ_ thao tác." + +#: src/help.c:114 +msgid "" +" MODE is either \"transparent\" or \"auth\"" +msgstr "" +" CHẾ ĐỘ đó là hoặc \"transparent\" (trong " +"suốt)\n" +"\t\t\t\t\t\t\thay \"auth\" (xác thực)" + +#: src/help.c:115 +msgid " -f, --foreground Foreground mode." +msgstr " -f, --foreground Chế độ _cảnh gần_." + +#: src/help.c:116 +msgid "" +" -i, --stdio Use the SMTP protocol (OMP/Tunnel) as " +"described\n" +" in RFC 821 on standard input and output." +msgstr "" +" -i, --stdio Sử dụng giao thức SMTP (OMP/Tunnel) như được " +"diễn tả\n" +"\t\t\t\t\ttrong RFC 821, với _thiết bị gõ/xuất chuẩn_." + +#: src/help.c:118 +msgid "Output options:\n" +msgstr "Tùy chọn xuất:\n" + +#: src/help.c:119 +msgid " -s, --silent Work silently." +msgstr " -s, --silent Không xuất chi tiết (_im_)." + +#: src/help.c:120 +msgid " -v, --verbose Work noisily." +msgstr " -v, --verbose Xuất _chi tiết_." + +#: src/help.c:121 +msgid " -D, --debug Debug mode." +msgstr " -D, --debug Chế độ _gỡ lỗi_." + +#: src/help.c:122 +msgid "" +"\n" +"Miscellaneous options:\n" +msgstr "" +"\n" +"Tùy chọn thêm:\n" + +#: src/help.c:123 +msgid "" +" -c, --check-config Run the configuration file syntax checker." +msgstr "" +" -c, --check-config Chạy trình _kiểm tra_ cú pháp trong tập tin " +"_cấu hình_." + +#: src/help.c:124 +msgid "" +" --show-config-options Print a list of configuration options used\n" +" to build GNU Anubis." +msgstr "" +" --show-config-options _Hiển thị_ danh sách _tùy chọn cấu hình_\n" +"\t\t\t\t\t\tđược dùng để xây dụng trình Anubis của GNU." + +#: src/help.c:126 +msgid "" +" --relax-perm-check Do not check user configuration file " +"permissions." +msgstr "" +" --relax-perm-check Không _kiểm tra quyền truy cập_ tập tin cấu " +"hình\n" +"\t\t\t\t\t\tngười dùng (_nới lỏng_)." + +#: src/help.c:127 +msgid "" +" --altrc FILE Specify alternate system configuration file." +msgstr "" +" --altrc TẬP_TIN Ghi rõ tập tin đó là tập tin cấu hình hệ thống " +"_thay thế_." + +#: src/help.c:128 +msgid " --norc Ignore system configuration file." +msgstr " --norc Bỏ qua tập tin cấu hình hế thống." + +#: src/help.c:129 +msgid " --version Print version number and copyright." +msgstr "" +" --version Hiển thị số _phiên bản_ và thông tin quyền." + +#: src/help.c:130 +msgid " --help It's obvious..." +msgstr " --help trợ _giúp_" + +#: src/help.c:131 +#, c-format +msgid "" +"\n" +"Report bugs to <%s>.\n" +msgstr "" +"\n" +"Hãy thông báo lỗi cho <%s>.\n" + +#: src/ident.c:105 +msgid "IDENT: socket() failed" +msgstr "IDENT: socket() (ổ cắm) không thành công" + +#: src/ident.c:117 +msgid "IDENT: connect() failed" +msgstr "IDENT: connect() (kết nối) không thành công" + +#: src/ident.c:123 +#, c-format +msgid "IDENT: connected to %s:%u" +msgstr "IDENT: hiện có kết nối đến %s:%u" + +#: src/ident.c:132 +#, c-format +msgid "IDENT: stream_write() failed: %s." +msgstr "IDENT: stream_write() (ghi dòng) không thành công: %s." + +#: src/ident.c:140 +#, c-format +msgid "IDENT: recvline() failed: %s." +msgstr "IDENT: recvline() (dòng nhận) không thành công: %s." + +#: src/ident.c:151 +msgid "IDENT: incorrect data." +msgstr "IDENT: dữ liệu không đúng." + +#: src/ident.c:162 +msgid "IDENT: data probably encrypted with DES..." +msgstr "IDENT: rất có thể là dữ liệu được mật mã bằng DES..." + +#: src/ident.c:170 +msgid "IDENT: incorrect data (DES deciphered)." +msgstr "IDENT: dữ liệu không đúng (đã giải mật mã DES)." + +#: src/ident.c:188 +#, c-format +msgid "IDENT: resolved remote user to %s." +msgstr "IDENT: đã quyết định người dùng ở xa là %s." + +#: src/map.c:71 +#, c-format +msgid "%s remapped to %s@localhost." +msgstr "Đã ảnh xạ lại %s thành %s@localhost." + +#: src/map.c:123 +msgid "Translation map: incorrect syntax." +msgstr "Bản đồ thông dịch: cú pháp không đúng." + +#: src/mem.c:37 +msgid "malloc() failed. Cannot allocate enough memory." +msgstr "malloc() (phân chia bộ nhớ) không thể phân chia đủ bộ nhớ." + +#: src/mem.c:52 +msgid "realloc() failed. Cannot reallocate enough memory." +msgstr "realloc() (phân chia lại bộ nhớ) không thể phân chia lại đủ bộ nhớ." + +#: src/misc.c:332 +msgid "Can't find out my own hostname" +msgstr "Không tìm thấy tên máy mình" + +#: src/net.c:50 +msgid "SERVER" +msgstr "MÁY CHỦ" + +#: src/net.c:53 +msgid "CLIENT" +msgstr "MÁY KHÁCH" + +#: src/net.c:131 +msgid "Getting remote host information..." +msgstr "Gọi thông tin máy ở xa..." + +#: src/net.c:168 +msgid "Cannot create stream socket." +msgstr "Không thể tạo ổ cắm dòng." + +#: src/net.c:173 +#, c-format +msgid "Couldn't connect to %s:%u. %s." +msgstr "Không thể kết nối đến %s:%u. %s." + +#: src/net.c:178 +#, c-format +msgid "Connected to %s:%u" +msgstr "Hiện có kết nối đến %s:%u" + +#: src/net.c:200 +msgid "Cannot create stream socket" +msgstr "Không thể tạo ổ cắm dòng." + +#: src/net.c:233 +msgid "bind() failed" +msgstr "bind() (đóng kết) không thành công." + +#: src/net.c:234 +#, c-format +msgid "GNU Anubis bound to %s:%u" +msgstr "Trình Anubis của GNU đã đóng kết đến %s:%u" + +#: src/net.c:261 +msgid "Short write" +msgstr "Ghi vắn" + +#: src/net.c:339 +msgid "INTERNAL ERROR (get_response_smtp): buffer exhausted. Please report." +msgstr "" +"LỖI NỘI BỘ (get_response_smtp): (gọi trả lời SMTP) hết bộ đệm hoàn toàn. Hãy " +"thông báo lỗi này." + +#: src/socks.c:53 +msgid "Using SOCKS Proxy..." +msgstr "Dùng máy ủy nhiệm SOCKS..." + +#: src/socks.c:71 +#, c-format +msgid "SOCKS proxy: %s" +msgstr "Máy ủy nhiệm SOCKS: %s" + +#: src/socks.c:143 +msgid "Address must be an IP, not a domain name." +msgstr "" +"Địa chỉ phải là địa chỉ IP (v.d. 127.0.0.0), không phải tên miền (v.d. www." +"miềnnày.com)." + +#: src/socks.c:180 src/socks.c:399 +msgid "SOCKS Proxy Connection: succeeded." +msgstr "Kết nối ủy nhiệm SOCKS: thành công." + +#: src/socks.c:183 +msgid "Request rejected or failed." +msgstr "Yêu cầu bị từ chối hay không thành công." + +#: src/socks.c:186 +msgid "Request rejected." +msgstr "Yêu cầu bị từ chối." + +#: src/socks.c:189 +msgid "" +"Request rejected, because the client program and identd reported different " +"User-IDs." +msgstr "" +"Yêu cầu bị từ chối, vì chương trình khách và identd đã thông báo thông tin " +"nhận biết người dùng (UID) khác nhau." + +#: src/socks.c:193 src/socks.c:426 +msgid "Server reply is not valid." +msgstr "Máy phục vụ trả lời không hợp lệ." + +#: src/socks.c:234 +msgid "Possibly not a SOCKS proxy service." +msgstr "Có lẽ không phải một dịch vụ ủy nhiệm SOCKS." + +#: src/socks.c:246 +msgid "SOCKS Proxy AUTH method: NO AUTHENTICATION REQUIRED" +msgstr "Phương pháp xac thức (AUTH) ủy nhiệm SOCKS: KHÔNG CẦN PHẢI XÁC THỨC" + +#: src/socks.c:249 +msgid "SOCKS Proxy AUTH method: USER NAME/PASSWORD" +msgstr "Phương pháp xac thức (AUTH) ủy nhiệm SOCKS: TÊN DÙNG/MẬT KHẨU" + +#: src/socks.c:253 +msgid "Cannot send null user name or password." +msgstr "Không gởi được tên dùng hay mật khẩu rỗng." + +#: src/socks.c:295 +msgid "Bad user name or password." +msgstr "Tên dùng hay mật khẩu sai." + +#: src/socks.c:299 +msgid "SOCKS Proxy AUTH: succeeded." +msgstr "Xac thức (AUTH) ủy nhiệm SOCKS: thành công." + +#: src/socks.c:302 +msgid "Server does not accept any method." +msgstr "Máy phục vụ không chấp nhận phương pháp nào." + +#: src/socks.c:305 +msgid "Server does not accept an AUTH method." +msgstr "Máy phục vụ không chấp nhận phương pháp AUTH (xác thực)." + +#: src/socks.c:402 +msgid "General SOCKS server failure." +msgstr "Lỗi máy phục vụ SOCKS chung." + +#: src/socks.c:405 +msgid "Connection not allowed by a ruleset." +msgstr "Một bộ quy tắc không cho phép kết nối đó." + +#: src/socks.c:408 +msgid "Network unreachable." +msgstr "Không tớí được mạng." + +#: src/socks.c:411 +msgid "Host unreachable." +msgstr "Không tới được máy." + +#: src/socks.c:414 +msgid "Connection refused." +msgstr "Kết nối bị từ chối." + +#: src/socks.c:417 +msgid "TTL expired." +msgstr "Thời gian sống đã hết hạn." + +#: src/socks.c:420 +msgid "Command not supported." +msgstr "Không hỗ trợ lệnh đó." + +#: src/socks.c:423 +msgid "Address type not supported." +msgstr "Không hỗ trợ kiểu địa chỉ đó." + +#: src/quit.c:31 +msgid "Signal Caught. Exiting Cleanly..." +msgstr "Đã bắt tín hiệu, thoát được..." + +#: src/quit.c:38 +msgid "Timeout! Exiting..." +msgstr "Thời hạn! Thoát..." + +#: src/rcfile.c:121 +#, c-format +msgid "cannot stat file `%s'" +msgstr "không thể stat (gọi các thông tin về) tập tin `%s'" + +#: src/rcfile.c:131 +#, c-format +msgid "File `%s' has already been read.\n" +msgstr "Tập tin « %s » đã được đọc.\n" + +#: src/rcfile.c:149 +#, c-format +msgid "Welcome user %s !" +msgstr "Chào mừng người dùng %s !" + +#: src/rcfile.c:185 +#, c-format +msgid "Reading system configuration file %s..." +msgstr "Đọc tập tin cấu hình hệ thống %s..." + +#: src/rcfile.c:197 +#, c-format +msgid "Reading user configuration file %s..." +msgstr "Đọc tập tin cấu hình người dùng %s..." + +#: src/rcfile.c:341 +msgid "`logfile' directive is ignored in main configuration file" +msgstr "" +"Chỉ thị `logfile' (tập tin bản ghi) bị bỏ qua trong tập tin cấu hình chính." + +#: src/rcfile.c:789 +#, c-format +msgid "No such section: %s" +msgstr "Không có phần như : %s" + +#: src/rcfile.l:181 +#, c-format +msgid "" +"Stray character in config: \\%03o. Possibly missing quotes around the string" +msgstr "" +"Gặp ký tự rải rác trong cấu hình: \\%03o. Có lẽ thiếu dấu trích dẫn ở quanh " +"chuỗi." + +#: src/rcfile.l:385 +msgid "Anubis RC file error" +msgstr "Lỗi tập tin RC Anubis." + +#: src/rcfile.y:170 src/rcfile.y:178 +#, c-format +msgid "Section %s already defined" +msgstr "Phần %s đã được định nghĩa." + +#: src/rcfile.y:230 +#, c-format +msgid "unknown keyword: %s" +msgstr "không biết từ khoá: %s" + +#: src/rcfile.y:532 +msgid "missing replacement value" +msgstr "thiếu giá trị thay thế" + +#: src/rcfile.y:629 +#, c-format +msgid "Not a valid debugging level: %s" +msgstr "Không phải là mức độ gỡ lỗi hợp lệ: %s" + +#: src/rcfile.y:1179 +msgid "Unknown regexp modifier" +msgstr "Không biết ký tự sửa đổi biểu thức chính quy" + +#: src/rcfile.y:1277 +msgid "STOP" +msgstr "NGỪNG" + +#: src/rcfile.y:1282 +#, c-format +msgid "Calling %s" +msgstr "Đang gọi %s" + +#: src/rcfile.y:1288 +#, c-format +msgid "ADD %s [%s] %s" +msgstr "THÊM %s [%s] %s" + +#: src/rcfile.y:1298 +#, c-format +msgid "MODIFY %s [%s] [%s] %s" +msgstr "SỬA ĐỔI %s [%s] [%s] %s" + +#: src/rcfile.y:1311 +#, c-format +msgid "REMOVE HEADER [%s]" +msgstr "GỠ BỎ DÒNG ĐẦU [%s]" + +#: src/rcfile.y:1334 +#, c-format +msgid "Executing %s" +msgstr "Đang thực hiện %s" + +#: src/rcfile.y:1414 +#, c-format +msgid "Matched trigger \"%s\"" +msgstr "Thủ tục lẫy đã khớp « %s »" + +#: src/rcfile.y:1418 +#, c-format +msgid "Matched condition %s[%s] \"%s\"" +msgstr "Điều kiện đã khớp %s[%s] « %s »" + +#: src/rcfile.y:1528 +#, c-format +msgid "Section %s" +msgstr "Phần %s" + +#: src/rcfile.y:1552 +#, c-format +msgid "Unknown section: %s" +msgstr "Không biết phần: %s" + +#: src/rcfile.y:1599 +msgid "program is not allowed in this section" +msgstr "không cho phép chương trình đó trong phần này" + +#: src/regex.c:113 +#, c-format +msgid "INTERNAL ERROR at %s:%d: missing or invalid regex" +msgstr "LỖI NỘI BỘ tại %s:%d: thiếu biểu thức chính quy hay nó không hợp lệ" + +#: src/regex.c:332 +#, c-format +msgid "regcomp() failed at %s: %s." +msgstr "regcomp() không thành công tại %s: %s." + +#: src/regex.c:408 +#, c-format +msgid "pcre_compile() failed at offset %d: %s." +msgstr "pcre_compile() không thành công tại hiệu số %d: %s." + +#: src/regex.c:433 +#, c-format +msgid "pcre_fullinfo() failed: %d." +msgstr "pcre_fullinfo() không thành công: %d." + +#: src/regex.c:445 +msgid "Matched, but too many substrings." +msgstr "Khớp được, nhưng mà có quá nhiều chuỗi phụ." + +#: src/regex.c:458 +#, c-format +msgid "Get substring %d failed (%d)." +msgstr "Gọi chuỗi phụ %d không thành công (%d)." + +#: src/ssl.c:59 +msgid "Seeding random number generator..." +msgstr "Chèn bộ tạo số ngẫu nhiên..." + +#: src/ssl.c:63 +msgid "Unable to seed random number generator." +msgstr "Không chèn được bộ tạo số ngẫu nhiên." + +#: src/ssl.c:80 +#, c-format +msgid "Write error: %s" +msgstr "Lỗi ghi: %s" + +#: src/ssl.c:98 +#, c-format +msgid "Read error: %s" +msgstr "Lỗi đọc: %s" + +#: src/ssl.c:277 +msgid "SSLv23_client_method() failed." +msgstr "SSLv23_client_method() (phương pháp máy khách) không thành công." + +#: src/ssl.c:282 src/ssl.c:363 +msgid "Can't create SSL_CTX object." +msgstr "Không thể tạo đối tượng SSL_CTX." + +#: src/ssl.c:288 src/ssl.c:383 +msgid "SSL_CTX_set_cipher_list() failed." +msgstr "SSL_CTX_set_cipher_list() (lập danh sách mật mã) không thành công." + +#: src/ssl.c:309 src/tls.c:168 +msgid "Initializing the TLS/SSL connection with MTA..." +msgstr "Khởi động kết nối TLS/SSL với MTA..." + +#: src/ssl.c:313 src/ssl.c:404 +msgid "Can't create a new SSL structure for a connection." +msgstr "Không thể tạo cấu trúc SSL mới để kết nối." + +#: src/ssl.c:329 src/tls.c:202 +#, c-format +msgid "TLS/SSL handshake failed: %s" +msgstr "Việc bắt tay TLS/SSL không thành công: %s" + +#: src/ssl.c:358 +msgid "SSLv23_server_method() failed." +msgstr "SSLv23_server_method() (phương pháp máy phục vụ) không thành công." + +#: src/ssl.c:368 +msgid "SSL_CTX_use_certificate_file() failed." +msgstr "" +"SSL_CTX_use_certificate_file() (dùng tập tin chứng nhận) không thành công." + +#: src/ssl.c:373 +msgid "SSL_CTX_use_PrivateKey_file() failed." +msgstr "" +"SSL_CTX_use_PrivateKey_file() (dùng tập tin khoá riêng) không thành công." + +#: src/ssl.c:378 +msgid "Private key does not match the certificate public key." +msgstr "Khoá riêng không khớp khoá công của chứng nhận." + +#: src/ssl.c:400 src/tls.c:252 +msgid "Initializing the TLS/SSL connection with MUA..." +msgstr "Khởi động kết nối TLS/SSL vớí MUA..." + +#: src/ssl.c:417 src/tls.c:284 +msgid "TLS/SSL handshake failed!" +msgstr "Việc bắt tây TLS/SSL không thành công." + +#: src/ssl.c:450 +#, c-format +msgid "%s connection using %s (%u bit)" +msgid_plural "%s connection using %s (%u bits)" +msgstr[0] "%s kết nối dùng %s (%u bit)" + +#: src/ssl.c:462 +#, c-format +msgid "Server public key is %d bit" +msgid_plural "Server public key is %d bits" +msgstr[0] "Khoá công máy phục vụ là %d bit" + +#: src/ssl.c:467 +msgid "Certificate:" +msgstr "Chứng nhận:" + +#: src/ssl.c:472 +msgid "X509_NAME_oneline [subject] failed!" +msgstr "X509_NAME_oneline [subject] (tên dòng đơn [chủ đề]) không thành công." + +#: src/ssl.c:475 +#, c-format +msgid "Subject: %s" +msgstr "Chủ đề: %s" + +#: src/ssl.c:479 +msgid "X509_NAME_oneline [issuer] failed!" +msgstr "" +"X509_NAME_oneline [issuer] (tên dòng đơn [nhà phat hành] không thành công." + +#: src/ssl.c:482 +#, c-format +msgid "Issuer: %s" +msgstr "Nhà phát hành: %s" + +#: src/tls.c:185 src/tls.c:263 +#, c-format +msgid "TLS error reading `%s': %s" +msgstr "Gặp lỗi TLS khi đọc `%s': %s" + +#: src/tls.c:305 +msgid "No certificate was sent." +msgstr "Chưa gởi chứng nhận." + +#: src/tls.c:310 +msgid "The certificate is not trusted." +msgstr "Không tin chứng nhận đó." + +#: src/tls.c:315 +msgid "The certificate has expired." +msgstr "Chứng nhận đó đã hết hạn." + +#: src/tls.c:320 +msgid "The certificate is not yet activated." +msgstr "Chưa hoạt hóa chứng nhận đó." + +#: src/tls.c:330 +msgid "No certificate was found!" +msgstr "Chưa tìm thấy chứng nhận." + +#: src/tls.c:335 +msgid "The certificate is trusted." +msgstr "Tin chứng nhận đó." + +#: src/tls.c:365 +#, c-format +msgid "- Anonymous DH using prime of %d bit.\n" +msgid_plural "- Anonymous DH using prime of %d bits.\n" +msgstr[0] "- DH vô danh dùng số nguyên tố của %d bit.\n" + +#: src/tls.c:373 +#, c-format +msgid "- Ephemeral DH using prime of %d bit.\n" +msgid_plural "- Ephemeral DH using prime of %d bits.\n" +msgstr[0] "- DH phù du dùng số nguyên tố của %d bit.\n" + +#: src/tls.c:384 +#, c-format +msgid "- Protocol: %s\n" +msgstr "- Giao thức: %s\n" + +#: src/tls.c:388 +#, c-format +msgid "- Certificate Type: %s\n" +msgstr "- Kiểu chứng nhận: %s\n" + +#: src/tls.c:391 +#, c-format +msgid "- Compression: %s\n" +msgstr "- Nén: %s\n" + +#: src/tls.c:394 +#, c-format +msgid "- Cipher: %s\n" +msgstr "- Mật mã: %s\n" + +#: src/tls.c:397 +#, c-format +msgid "- MAC: %s\n" +msgstr "- MAC: %s\n" + +#: src/tls.c:425 +#, c-format +msgid "- Certificate info:\n" +msgstr "- Thông tin chứng nhận:\n" + +#: src/tls.c:429 +#, c-format +msgid "- Certificate is valid since: %s" +msgstr "- Chứng nhận đó hợp lệ sau: %s" + +#: src/tls.c:431 +#, c-format +msgid "- Certificate expires: %s" +msgstr "- Chứng nhận đó hết hạn: %s" + +#: src/tls.c:436 +#, c-format +msgid "- Certificate fingerprint: " +msgstr "- Dấu điềm chỉ chứng nhận: " + +#: src/tls.c:446 +#, c-format +msgid "- Certificate serial number: " +msgstr "- Số sản xuất chứng nhận: " + +#: src/tls.c:455 +#, c-format +msgid "- Certificate public key: " +msgstr "- Khoá công chứng nhận: " + +#: src/tls.c:458 +#, c-format +msgid "RSA\n" +msgstr "RSA\n" + +#: src/tls.c:459 +#, c-format +msgid "- Modulus: %d bit\n" +msgid_plural "- Modulus: %d bits\n" +msgstr[0] "- Giá trị tuyệt đối: %d bit\n" + +#: src/tls.c:464 +#, c-format +msgid "DSA\n" +msgstr "DSA\n" + +#: src/tls.c:465 +#, c-format +msgid "- Exponent: %d bit\n" +msgid_plural "- Exponent: %d bits\n" +msgstr[0] "- Số mũ : %d bit\n" + +#: src/tls.c:469 +#, c-format +msgid "UNKNOWN\n" +msgstr "KHÔNG BIẾT\n" + +#: src/tls.c:471 +#, c-format +msgid "- Certificate version: #%d\n" +msgstr "- Phiên bản chứng nhận: #%d\n" + +#: src/tls.c:478 +#, c-format +msgid "- Certificate Issuer's DN: %s\n" +msgstr "- Tên miền của nhà phát hành chứng nhận: %s\n" + +#: src/tunnel.c:318 src/tunnel.c:387 +msgid "Transferring message(s)..." +msgstr "Truyền thông điệp..." + +#: src/tunnel.c:385 +msgid "Starting SMTP session..." +msgstr "Bắt đầu phiên chạy SMTP..." + +#: src/tunnel.c:468 +msgid "Using the TLS/SSL encryption..." +msgstr "Dùng mật mã TLS/SSL..." + +#: src/tunnel.c:482 src/tunnel.c:626 +#, c-format +msgid "WARNING: %s" +msgstr "CẢNH BÁO : %s" + +#: src/tunnel.c:483 +msgid "STARTTLS command failed." +msgstr "Lệnh STARTTLS không thành công." + +#: src/tunnel.c:617 +msgid "Using TLS/SSL encryption between Anubis and remote MTA only..." +msgstr "Dùng mật mã TLS/SSL chỉ giữa trình Anubis và MTA ở xa thôi..." + +#: src/tunnel.c:627 +msgid "STARTTLS (ONEWAY) command failed." +msgstr "Lệnh STARTTLS (ONEWAY) (chỉ một chiều) không thành công." + +#: ap-gl/ap-gl.c:41 ap-gl/bridge.c:178 src/ap-config.c:41 src/bridge.c:182 +msgid "Bridging" +msgstr "Cầu dẫn" + +#: ap-gl/ap-gl.c:41 src/ap-config.c:41 +msgid "Set bridging and IP-related options" +msgstr "" +"Lập các tùy chọn cầu dẫn và các tùy chọn liên quân đến giao thức Mạng (IP)." + +#: ap-gl/ap-gl.c:43 ap-gl/ap-gl.c:81 ap-gl/bridge.c:132 src/ap-config.c:43 +#: src/ap-config.c:112 src/ap-config.c:120 src/bridge.c:117 src/bridge.c:121 +#: sysinfo.c:466 sysinfo.c:467 src/ui.glade.h:69 src/monitor-impls.cpp:709 +#: ../sheets/ciscocomputer.sheet.in.h:50 +#, c-format +msgid "Wireless" +msgstr "Vô tuyến" + +#: ap-gl/ap-gl.c:43 src/ap-config.c:43 +msgid "Set wireless options" +msgstr "Lập các tùy chọn vô tuyến." + +#: ui/prefs-dialog.glade.h:54 ../ui/user_info.glade.h:58 +msgid "Privacy" +msgstr "Riêng tư" + +#: ap-gl/ap-gl.c:45 src/ap-config.c:45 src/ap-config.c:57 +msgid "MAC auth" +msgstr "Xác thực MAC" + +#: ap-gl/ap-gl.c:46 src/ap-config.c:46 src/ap-config.c:58 ../src/dialogs.c:774 +msgid "Community" +msgstr "Cộng đồng" + +#: ap-gl/ap-gl.c:47 src/ap-config.c:47 +msgid "Set radio signal power and antenna options" +msgstr "Lập năng lượng tín hiệu thu thanh và các tùy chọn ăngten." + +#: ap-gl/ap-gl.c:62 src/ap-config.c:80 +msgid "Upload" +msgstr "Tải lên" + +#: ap-gl/ap-gl.c:62 src/ap-config.c:80 +msgid "Activate current configuration" +msgstr "Hoạt hóa cấu hình hiện có" + +#: ap-gl/ap-gl.c:63 src/ap-config.c:81 +msgid "Restore factory default settings" +msgstr "Phục hồi các thiết lập mặc định của hãng" + +#: web/template/keywords_view_bottom.tpl:2 ../src/glade-editor.c:766 +#: src/settings.c:1506 +msgid "Reset" +msgstr "Đặt lại" + +#: ap-gl/ap-gl.c:65 src/ap-config.c:83 +msgid "Reset AP. All not uploaded configuration will be lost" +msgstr "" +"Lập lại Điểm Truy cập. Như thế thì mọi cấu hình chưa tải lên sẽ bị mất." + +#: ap-gl/ap-gl.c:66 src/ap-config.c:84 +msgid "TestMode" +msgstr "Chế độ thử" + +#: ap-gl/ap-gl.c:66 src/ap-config.c:84 +msgid "Put Access Point in test mode" +msgstr "Đặt Điểm Truy cập trong chế độ thử ra" + +#: ap-gl/ap-gl.c:79 src/ap-config.c:110 src/ap-config.c:119 +msgid "SysInfo" +msgstr "Thông tin hệ thống" + +#: ../src/netstatus-iface.c:880 +msgid "Ethernet" +msgstr "Ethernet" + +#: ap-gl/ap-gl.c:80 src/ap-config.c:111 +msgid "Get ethernet port statistics" +msgstr "Gọi thống kê cổng Ethernet" + +#: ap-gl/ap-gl.c:82 src/ap-config.c:113 src/ap-config.c:121 +msgid "Stations" +msgstr "Trạm" + +#: ap-gl/ap-gl.c:83 src/ap-config.c:114 +msgid "KnownAPs" +msgstr "Điểm TC đã biết" + +#: ap-gl/ap-gl.c:83 src/ap-config.c:114 +msgid "Get info about known Access Points" +msgstr "Gọi thông tin về các Điểm Truy cập được biết" + +#: src/fe-gtk/dccgui.c:586 src/fe-gtk/dccgui.c:744 +#: ../widgets/gtk+.xml.in.h:105 libexif/olympus/mnote-olympus-tag.c:113 +msgid "Info" +msgstr "Thông tin" + +#: ap-gl/ap-gl.c:101 src/ap-config.c:148 +msgid "Config" +msgstr "Cấu hình" + +#: ap-gl/ap-gl.c:102 src/ap-config.c:149 +msgid "Execute commands on Access Point" +msgstr "Thực hiện lệnh vơi Điểm Truy cập" + +#: ../glom/glom.glade.h:78 ../ui/connect.glade.h:5 +msgid "Connect" +msgstr "Kết nối" + +#: info/session.c:3672 info/session.c:3678 ../ui/mlview-search-node.glade.h:3 +#: ../scripts/test.c:309 ../glade/search.glade.h:7 ../glade/straw.glade.h:54 +#: search_gui.c:526 search_gui.c:579 po/silky.glade.h:160 +msgid "Search" +msgstr "Tìm kiếm" + +#: ../src/Win_GParted.cc:112 src/interface.c:733 ../ui/user_info.glade.h:7 +#: ../pan/dialogs/pan-about.c:167 +msgid "About" +msgstr "Giới thiệu" + +#: ap-gl/ap-gl.c:107 src/ap-config.c:154 ../src/users/users-table.c:65 +msgid "Shell" +msgstr "Hệ vỏ" + +#: web/template/auth.tpl:3 +msgid "Exit" +msgstr "Thoát" + +#: ap-gl/ap-gl.c:158 src/ap-config.c:204 +#, c-format +msgid "Wireless Access Point Configurator ver. %s" +msgstr "Bộ cấu hình Điểm Truy cập Vô tuyến phiên bản %s" + +#: ap-gl/auth_mac.c:27 src/auth_mac.c:27 +msgid "AuthorizedMacTableString packet error" +msgstr "Lỗi gói tin AuthorizedMacTableString (chuỗi bảng MAC đã xác thực)" + +#: ap-gl/auth_mac.c:29 src/auth_mac.c:29 +msgid "[A] MAC authorization: " +msgstr "[A] Xác thực MAC: " + +#: ap-gl/auth_mac.c:30 src/auth_mac.c:30 +msgid "Enter MAC: " +msgstr "Hãy nhập MAC: " + +#: ap-gl/auth_mac.c:31 src/auth_mac.c:31 +msgid "Delete Num: " +msgstr "Xoá bỏ số :" + +#: ap-gl/auth_mac.c:32 src/auth_mac.c:32 +msgid "Authorized MAC addresses" +msgstr "Các địa chỉ MAC đã xác thực" + +#: ap-gl/auth_mac.c:33 +msgid "NUM MAC address" +msgstr "SỐ địa chỉ MAC" + +#: ap-gl/auth_mac.c:34 src/auth_mac.c:34 +msgid "A - auth; N - new; D - del; arrows - scroll; W - write conf; Q - quit" +msgstr "" +"A - xác thực; N - mới; D - xoá bỏ; mũi tên - cuộn; W - ghi cấu hình; Q - " +"thoát" + +#: ap-gl/auth_mac.c:35 +msgid "A - auth; IPSTF - set; W - write conf; Q - quit" +msgstr "A - xác thực; IPSTF - lập; W - ghi cấu hình; Q - thoát" + +#: ap-gl/auth_mac.c:36 +msgid "A - auth; W - write conf; Q - quit" +msgstr "A - xác thực; W - ghi cấu hình; Q - thoát" + +#: ap-gl/auth_mac.c:38 +msgid "[I] RADIUS SERVER IP: " +msgstr "[I] ĐỊA CHỈ IP MÁY CHỦ RADIUS: " + +#: ap-gl/auth_mac.c:39 +msgid "[P] RADIUS SERVER PORT: " +msgstr "[P] CỔNG MÁY CHỦ RADIUS: " + +#: ap-gl/auth_mac.c:40 +msgid "[S] RADIUS SERVER SECRET: " +msgstr "[S] BỊ MẤT MÁY CHỦ RADIUS: " + +#: ap-gl/auth_mac.c:41 +msgid "[T] REAUTHORIZATION TIME: " +msgstr "[T] THỜI GIAN XÁC THỨC LẠI: " + +#: ap-gl/auth_mac.c:42 +msgid "[F] RADIUS SOURCE PORT: " +msgstr "[F] CỔNG NGUỒN RADIUS: " + +#: ap-gl/auth_mac.c:87 +msgid "Internal" +msgstr "Nộị bộ" + +#: ap-gl/auth_mac.c:95 +msgid "" +msgstr "" + +#: ap-gl/bridge.c:26 src/bridge.c:26 +msgid "[I] IP: " +msgstr "[I] Địa chỉ IP: " + +#: ap-gl/bridge.c:27 src/bridge.c:27 +msgid "[N] Netmask: " +msgstr "[N] Mặt nạ mạng: " + +#: ap-gl/bridge.c:28 src/bridge.c:28 +msgid "[G] Gateway: " +msgstr "[G] Cổng ra: " + +#: ap-gl/bridge.c:29 src/bridge.c:29 +msgid "[F] Filter non-IP traffic: " +msgstr "[F] Lọc các tải khác IP: " + +#: ap-gl/bridge.c:30 src/bridge.c:30 +msgid "[P] Primary port: " +msgstr "[P] Cổng chính:" + +#: ap-gl/bridge.c:31 src/bridge.c:31 +msgid "Attached station MAC: " +msgstr "MAC trạm đã gắn:" + +#: ap-gl/bridge.c:32 src/bridge.c:32 +msgid "[D] DHCP client: " +msgstr "[D] Máy khách DHCP:" + +#: ap-gl/bridge.c:33 src/bridge.c:33 +msgid "[O] Operational mode: " +msgstr "[O] Chế đô thao tác:" + +#: ap-gl/bridge.c:34 src/bridge.c:34 +msgid "[M] Preferred BSSID (remote MAC addr.): " +msgstr "[M] BSSID ưa thích (địa chỉ MAC ở xa): " + +#: ap-gl/bridge.c:36 src/bridge.c:36 +msgid "[T] Trap-sending port(s): " +msgstr "[T] Cổng bắt gởi:" + +#: ap-gl/bridge.c:37 src/bridge.c:37 +msgid "[R] Forward broadcast traffic: " +msgstr "[R] Chuyển tiếp tải phát thanh:" + +#: ap-gl/bridge.c:39 +msgid "[U] Isolate wireless clients: " +msgstr "[U] Cách các máy/trình khách vô tuyến:" + +#: ap-gl/bridge.c:40 src/bridge.c:40 +msgid "INGFPDOMSCTRBU - set; W - write conf; Q - quit to menu" +msgstr "INGFPDOMSCTRBU - lập; W - ghi cấu hình Q - thoát vào trình đơn" + +#: ap-gl/bridge.c:124 src/bridge.c:109 +msgid "Wireless Bridge Point to MultiPoint" +msgstr "Điểm Cấu dẫn Vô tuyến đến Đa Điểm" + +#: ap-gl/bridge.c:125 src/bridge.c:110 +msgid "Access Point" +msgstr "Điểm Truy cập" + +#: ap-gl/bridge.c:126 src/bridge.c:111 +msgid "Access Point client" +msgstr "Máy/trình khách Điểm Truy cập" + +#: ap-gl/bridge.c:127 src/bridge.c:112 +msgid "Wireless Bridge Point to Point" +msgstr "Điểm-đến-Điểm Cẫu dẫn Vô tuyến" + +#: ap-gl/bridge.c:128 src/bridge.c:113 ../sheets/cisconetwork.sheet.in.h:76 +msgid "Repeater" +msgstr "Bộ lặp lại" + +#: ap-gl/stations.c:77 src/stations.c:100 +msgid "AP is currently in AP Client Mode => no associated STAtions." +msgstr "" +"Điểm TC hiện có trong chế độ khách Điểm TC → không có trạm nào liên quân." + +#: ap-gl/stations.c:102 +msgid "# MAC LQ RSSI Status Port IP" +msgstr "# MAC LQ RSSI Trạngt Cổng IP" + +#: ap-gl/stations.c:128 src/stations.c:159 +msgid "AssociatedSTAsInfo packet error" +msgstr "Lỗi gói tin AssociatedSTAsInfo (thông tin cac trạm liên quan)" + +#: ap-gl/stations.c:160 +msgid "Arrows - scroll; S - save to file; Q - quit to menu." +msgstr "Mũi tên - cuộn; S - lưu vào tập tin; Q - thoát vào trình đơn." + +#: lib/aps.c:29 +msgid "Known Access Points" +msgstr "Các Điểm TC đã biết" + +#: lib/aps.c:129 +msgid "Your Access Point is not in \"AP client\" mode => getting" +msgstr "" +"Điểm Truy cập của bạn không phải trong chế độ « trình khách Điểm TC » → gọi" + +#: lib/aps.c:132 +msgid "up-to-date \"Known APs\" info requires your AP to be" +msgstr "tin tức « Các Điểm TC đã biết » cần thiết Điểm TC bạn được" + +#: lib/aps.c:135 +msgid "temporarily configured into \"AP client\" mode and rebooted." +msgstr "" +"cấu hình tạm thời vào chế độ « trình khách Điểm TC » rồi được khởi động lại." + +#: lib/aps.c:138 +msgid "Your AP will be reconfigured back to original mode by this" +msgstr "Tiện ích này sẽ cấu hình lại Điểm TC bạn vào chế độ trước" + +#: lib/aps.c:141 +msgid "utility once you quit the \"KnownAP\" view. This, in turn, may" +msgstr "một khi bạn thoát khung xem « Các Điểm TC đã biết». Hành động này" + +#: lib/aps.c:144 +msgid "cause loss of Access Point's current configuration." +msgstr "có lẽ sẽ làm cho Điểm TC mất cấu hình hiện có." + +#: lib/aps.c:148 +msgid "Do NOT answer \"Yes\" if you're connected to the Access Point" +msgstr "ĐỪNG trả lời « Có » nếu bạn đang kết nối đến Điêm Truy cập" + +#: lib/aps.c:151 +msgid "via its wireless port." +msgstr "qua cổng vô tuyến của nó." + +#: lib/aps.c:153 lib/cmd.c:45 lib/cmd.c:75 lib/test.c:91 +msgid "Do you want to continue? " +msgstr "Bạn có muốn tiếp tục không?" + +#: lib/aps.c:207 +msgid "NetworkType" +msgstr "Kiểu mạng" + +#: lib/aps.c:283 +msgid "Infrastructure" +msgstr "Hạ tầng cơ sở" + +#: lib/aps.c:343 lib/aps.c:351 lib/aps.c:359 +msgid "CN: Channel Name; P: Preambule Type (S: Short; L: Long);" +msgstr "CN: Tên kênh; P: Kiểu lời mở đầu (S: Vắn; L: Dài);" + +#: lib/aps.c:345 +msgid "RSSI: Radio Signal Strength Indicator [%]" +msgstr "RSSI: Chỉ báo độ mạnh tín hiệu rađiô [%]" + +#: lib/aps.c:347 lib/aps.c:355 +msgid "; LQ: Link Quality [%]" +msgstr "; LQ: chất lượng liên kết [%]" + +#: lib/aps.c:353 +msgid "RSSI: Radio Signal Strength Indicator [dBm]" +msgstr "RSSI: Chỉ báo độ mạnh tín hiệu rađiô [dBm]" + +#: lib/aps.c:361 +msgid "RSSI: Radio Signal Strength Indicator [raw]" +msgstr "RSSI: Chỉ báo độ mạnh tín hiệu rađiô [thô]" + +#: lib/aps.c:363 +msgid "; LQ: Link Q. [raw]" +msgstr "; LQ: Chất lương liên kết [thô]" + +#: lib/aps.c:369 +msgid "" +"# con. to AP #; R refresh with reset; T toggle; Q quit; Other = refr. w/o " +"reset" +msgstr "" +"# kết nối đến ĐTC #; R cập nhật có lập lại; T bật/tắt; Q thoát; Other = cập " +"nhật không có lập lại" + +#: lib/aps.c:371 +msgid "" +"# con. to AP #; R initiate AP scan; T toggle view; Q quit; Other = refresh " +"view" +msgstr "" +"# kết nối đến ĐTC #; R khởi chạy quét ĐTC; T bật/tất khung xem; Q thoát; " +"Other = cập nhật khung xem" + +#: lib/aps.c:500 lib/ap_search.c:167 +#, c-format +msgid "Failure in sendto(): %s. Press any key." +msgstr "Lỗi trong sendto() (gởi cho): %s. Hãy bấm bất cứ phím nào." + +#: lib/aps.c:509 +msgid "You have just initiated the AP scan. Be advised that it may" +msgstr "Bạn mới khởi chạy quét tìm Điểm Truy cập. Ghi chú là" + +#: lib/aps.c:512 +msgid "take a few seconds for your Access Point to find out some" +msgstr "Điểm Truy cập có lẽ sẽ mất vài giây để tìm một số giá trị," + +#: lib/aps.c:515 +msgid "values, so expect finishing the scan in about 5 seconds." +msgstr "thì sẽ quét xong được trong khoảng 5 giây." + +#: lib/aps.c:518 +msgid "Also note that your Access Point stops forwarding the network" +msgstr "Cũng hãy ghi chú là Điểm Truy cập bạn ngừng chuyển tiếp tải" + +#: lib/aps.c:521 +msgid "traffic while the scan is in progress, but restores itself" +msgstr "mạng trong khi quét, nhưng mà phục hồi tự nó đến" + +#: lib/aps.c:524 +msgid "to normal operation in time ranging up to 1 minute." +msgstr "thao tác bình thường trong thời gian đến 1 phút." + +#: lib/aps.c:527 +msgid "Hence, if you are connected to target Access Point via its" +msgstr "Vì vậy, nếu bạn đang kết nối đến Điểm Truy cập đích qua" + +#: lib/aps.c:530 +msgid "wireless port, you need to wait a bit longer" +msgstr "cổng vô tuyến của nó, thì cần phải chờ dài hơn một chút " + +#: lib/aps.c:533 +msgid "after pressing 'S'." +msgstr "sau khi bấm phím S." + +#: lib/ap_search.c:48 +msgid "Community name: " +msgstr "Tên cộng đồng:" + +#: lib/ap_search.c:49 +msgid " NUM IP ADDRESS MIB TYPE NAME" +msgstr " SỐ ĐỊA CHỈ IP KIỂU MIB TÊN" + +#: lib/ap_search.c:100 +msgid "Please wait while scanning, or press 'Q' to quit." +msgstr "Hãy đời trong khi quét, hay bấm phím Q để thoát." + +#: lib/ap_search.c:116 +msgid "Can't set broadcast option on socket. Press any key." +msgstr "Không thể lập tùy chọn phát thanh trên ổ cắm. Hãy bấm bất cứ phím nào." + +#: lib/ap_search.c:125 +msgid "Can't set multicast membership on socket. Press any key." +msgstr "" +"Không lập địa vị hội viên truyền một-nhiều trên ổ cắm. Hãy bấm bất cứ phím " +"nào." + +#: lib/ap_search.c:132 +msgid "Scanning via network interface:" +msgstr "Đang quét qua giao diện mạng:" + +#: lib/ap_search.c:133 +#, c-format +msgid " Index: %i" +msgstr " Chỉ mục: %i" + +#: lib/ap_search.c:135 +#, c-format +msgid " Name: %s" +msgstr " Tên: %s" + +#: lib/ap_search.c:137 +#, c-format +msgid " IP: %s" +msgstr " Địa chỉ IP: %s" + +#: lib/ap_search.c:146 +#, c-format +msgid "Scanning for AP with MIB type: %s" +msgstr "Đang quét tìm Điểm TC có kiểu MIB: %s" + +#: lib/ap_search.c:334 +msgid "Please enter SNMP community name that will be used for AP detection." +msgstr "Hãy nhập tên cộng đồng SNMP sẽ dùng để phát hiện Điểm TC." + +#: lib/ap_search.c:345 +msgid "Access Points Search" +msgstr "Tìm kiếm Điểm TC" + +#: lib/ap_search.c:363 +msgid "realloc() error." +msgstr "Lỗi realloc()." + +#: lib/ap_search.c:375 +msgid "Network interface discovery error." +msgstr "Lỗi phát minh giao diện mạng." + +#: lib/ap_search.c:450 +msgid "No local network interfaces found. Press any key." +msgstr "Chưa tìm thấy giao diện mạng địa phương. Hãy bấm bất cứ phím nào." + +#: lib/ap_search.c:452 +msgid "No directly reachable Access Points found. Press any key." +msgstr "" +"Chưa tìm thấy Điểm Truy cập có thể tới trực tiếp. Hãy bấm bất cứ phím nào." + +#: lib/ap_search.c:457 +msgid "Single-screen maximum number of APs found." +msgstr "Tìm thấy số tối đa Điểm TC cho một màn hinh riêng lẻ." + +#: lib/ap_search.c:460 +msgid "# - connect to AP; Q - quit" +msgstr "# - kết nối đến ĐTC; Q - thoát" + +#: lib/ap-utils.h:79 +msgid "MAC address: " +msgstr "Địa chỉ MAC:" + +#: lib/ap-utils.h:80 +msgid "[S] SNMP traps: " +msgstr "[S] Nơi bắt SNMP:" + +#: lib/ap-utils.h:82 +msgid "[C] Frequency channel: " +msgstr "[C] Kênh tần số :" + +#: lib/ap-utils.h:84 +msgid "Receive antenna:" +msgstr "Ăngten nhận:" + +#: lib/ap-utils.h:85 +msgid "[U] Left" +msgstr "[U] Trái" + +#: lib/ap-utils.h:86 +msgid "[I] Right" +msgstr "[I] Phải" + +#: lib/ap-utils.h:87 +msgid "Transmit antenna:" +msgstr "Ăngten gởi:" + +#: lib/ap-utils.h:88 +msgid "[O] Left" +msgstr "[O] Trái" + +#: lib/ap-utils.h:89 +msgid "[P] Right" +msgstr "[P] Phải" + +#: lib/ap-utils.h:90 +msgid "Diversity select:" +msgstr "Chọn tính nhiều dạng:" + +#: lib/ap-utils.h:91 +msgid "[T] Left" +msgstr "[T] Trái" + +#: lib/ap-utils.h:92 +msgid "[Y] Right" +msgstr "[Y] Phải" + +#: lib/ap-utils.h:97 ../storage/sunone-permissions-dialog.glade.h:22 +#: src/settings.c:727 +msgid "On" +msgstr "Bật" + +#: lib/ap-utils.h:98 src/fe-gtk/setup.c:140 src/galeon-prefs-dialog.c:434 +#: src/fe-gtk/menu.c:1408 src/settings.c:732 +#: libexif/olympus/mnote-olympus-entry.c:145 +msgid "Off" +msgstr "Tắt" + +#: lib/ap-utils.h:102 +msgid "Press any key to continue." +msgstr "Bấm bất cứ phím nào để tiếp tục." + +#: lib/ap-utils.h:103 +msgid "Q - quit to menu. T - toggle polling mode, Other key - force update." +msgstr "" +"Q - thoát vào trình đơn. T - bật/tắt chế độ kiểm soát vòng, Phím khác - buộc " +"cập nhật.." + +#: lib/ap-utils.h:105 +msgid "Unable to write data to AP. Press any key to continue." +msgstr "Không thể ghi dữ liệu vào Điểm TC. Bấm bất cứ phím nào để tiếp tục." + +#: lib/ap-utils.h:106 +msgid "Unable to retrieve (valid) data from AP. Press any key to continue." +msgstr "" +"Không thể lấy dữ liệu (hợp lệ) từ Điểm TC. Bấm bất cứ phím nào để tiếp tục." + +#: lib/ap-utils.h:107 +msgid "Trying to retrieve data from AP. Please wait..." +msgstr "Cố gọi dữ liệu từ Điểm TC. Hãy đời...." + +#: lib/ap-utils.h:108 +msgid "Writing data to AP. Please wait..." +msgstr "Đang ghi dữ liệu vào Điểm TC. Hãy đời..." + +#: lib/ap-utils.h:109 +msgid "Configuration written to the AP. Press any key to continue." +msgstr "Cấu hình đã được ghi vào Điểm TC. Bấm bất cứ phím nào để tiếp tục." + +#: lib/ap-utils.h:110 +msgid "select() function error. Press any key." +msgstr "Lỗi chức năng select(). Bấm bất cứ phím nào." + +#: lib/ap-utils.h:112 +msgid "Create socket error. Press any key." +msgstr "Lỗi tạo ổ cắm. Bấm bất cứ phím nào." + +#: lib/ap-utils.h:113 +msgid "Bind socket error. Press any key." +msgstr "Lỗi đóng kết ổ cắm. Bấm bất cứ phím nào." + +#: lib/ap-utils.h:115 +msgid "Back to main menu" +msgstr "Trở về trình đơn chính" + +#: lib/ap-utils.h:116 +msgid "Exit program" +msgstr "Thoát khỏi chương trình" + +#: lib/ap-utils.h:117 +msgid "Run subshell. To return type 'exit'." +msgstr "Chạy hệ vỏ con. Để trở về thì gõ 'exit' (thoát)." + +#: lib/ap-utils.h:118 +msgid "Short info about program" +msgstr "Thông tin vắn về chương trình" + +#: lib/ap-utils.h:119 +msgid "Find connected Access Points" +msgstr "Tìm các Điểm Truy cập đã kết nối" + +#: lib/ap-utils.h:120 +msgid "Set connection options: ip and community" +msgstr "Lập tùy chọn kết nối: giao thức Mạng (IP) và cộng đồng" + +#: lib/ap-utils.h:121 +msgid "Set encryption; edit WEP keys" +msgstr "Lập mật mã; sửa đổi khoá WEP" + +#: lib/ap-utils.h:122 +msgid "Set MAC authorization; edit MAC authorization table" +msgstr "Lâp xác thực MAC; sửa đổi bảng xác thực MAC" + +#: lib/ap-utils.h:123 +msgid "Set SNMP community/password for access to the AP" +msgstr "Lập cộng đồng/mật khẩu SNMP để truy cập Điểm TC" + +#: lib/ap-utils.h:124 +msgid "Get info about AP hardware and firmware" +msgstr "Gọi thông tin về phần cứng Điểm TC và phần vững" + +#: lib/ap-utils.h:125 +msgid "Get wireless port statistics" +msgstr "Gọi thống kê cổng vô tuyến" + +#: lib/ap-utils.h:126 +msgid "Get list of currently associated stations (Access Point clients)" +msgstr "Gọi danh sách các trạm liên quan hiện có (máy khách Điểm Truy cập)" + +#: lib/ap-utils.h:127 +msgid "Get info and statistics from AP" +msgstr "Gọi thông tin và thống kê từ Điểm TC" + +#: lib/ap-utils.h:128 +msgid "Set various configuration options" +msgstr "Lập nhiều tùy chọn cấu hình khác nhau" + +#: lib/ap-utils.h:130 +msgid "Associated stations" +msgstr "Trạm liên quan" + +#: lib/ap-utils.h:132 +msgid "Polling: on" +msgstr "Kiểm soát vòng: bật" + +#: lib/ap-utils.h:133 +msgid "Polling: off" +msgstr "Kiểm soát vòng: tắt" + +#: lib/cmd.c:40 +msgid "Restore factory default configuration" +msgstr "Phục hồi cấu hình mặc định của hãng" + +#: lib/cmd.c:43 +msgid "After restoring factory defaults your current configuration" +msgstr "Sau khi phục hồi mặc định của hãng thì cấu hình hiện có" + +#: lib/cmd.c:44 +msgid "will be lost." +msgstr "sẽ bị mất." + +#: lib/cmd.c:63 +msgid "Factory default settings loaded. Press any key to continue." +msgstr "" +"Đã tải các thiết lập mặc định của hãng. Bấm bất cứ phím nào để tiếp tục." + +#: lib/cmd.c:71 +msgid "Reset Access Point" +msgstr "Lập lại Điểm Truy cập" + +#: lib/cmd.c:74 +msgid "By reset you'll lose all non-uploaded configuration." +msgstr "Khi lập lại thì sẽ mất các cấu hình chưa tải lên." + +#: lib/cmd.c:85 +msgid "Access Point reset. Press any key to continue." +msgstr "Điểm Truy cập đã được đặt lại. Hãy bấm bất cứ phím nào để tiếp tục." + +#: lib/cmd.c:117 +msgid "Upload configuration" +msgstr "Tải lên cấu hình" + +#: lib/cmd.c:119 +msgid "You may need to upload the configuration only if you've" +msgstr "Thường bạn cần tải lên cấu hình chỉ nếu đã thay đổi" + +#: lib/cmd.c:121 +msgid "changed some option values before. Using this option may" +msgstr "một số tùy chọn sau lần tải lên cuối cùng. Dùng tùy chọn" + +#: lib/cmd.c:123 +msgid "cause loss of your current configuration." +msgstr "này có lẽ sẽ làm cho cấu hình hiện có bị mất." + +#: lib/cmd.c:135 +msgid "Configuration uploaded. Press any key to continue." +msgstr "Cấu hình đã được tải lên. Hãy bấm bất cứ phím nào để tiếp tục." + +#: lib/common.c:30 +msgid "Access Point IP-address: " +msgstr "Địa chỉ IP của Điểm Truy cập:" + +#: lib/common.c:31 +msgid "Password (community): " +msgstr "Mật khẩu (cộng đồng):" + +#: lib/common.c:32 +msgid "Autodetect AP MIB properties? " +msgstr "Tự động phát hiện các thuộc tính MIB của Điểm Truy cập không?" + +#: lib/common.c:33 +msgid "AP MIB type: " +msgstr "Kiểu MIB của Điểm TC:" + +#: lib/common.c:34 +msgid "AP MIB vendor extensions: " +msgstr "Phần mở rộng của nhà bán MIB Điểm TC:" + +#: lib/common.c:35 +msgid "Do you want to use AP's name as its label? " +msgstr "Bạn có muốn đặt tên Điểm TC là nhãn nó chứ?" + +#: lib/common.c:36 +msgid "Access Point label: " +msgstr "Nhãn Điểm Truy cập:" + +#: lib/common.c:37 +msgid "Save connect-settings: " +msgstr "Lưu các thiết lập kết nối:" + +#: lib/common.c:100 +#, c-format +msgid "From %s" +msgstr "Từ %s" + +#: lib/common.c:102 src/Controller.cc:83 ../main/__init__.py:85 +#, c-format, python-format +msgid "Version %s" +msgstr "Phiên bản %s" + +#: lib/common.c:105 +msgid "Written by Roman Festchook roma@polesye.net" +msgstr "Tác giả: Roman Festchook roma@polesye.net" + +#: lib/common.c:107 +msgid "Portions by Jan Rafaj aputils@cedric.unob.cz" +msgstr "Một số phần bởi Jan Rafaj aputils@cedric.unob.cz" + +#: lib/common.c:109 +msgid "Copyright (c) 2001-2004" +msgstr "Bản quyền © năm 2001-2004" + +#: lib/common.c:111 +msgid "Roman Festchook and Jan Rafaj" +msgstr "Roman Festchook và Jan Rafaj" + +#: lib/common.c:114 +msgid "This program is distributed under the terms" +msgstr "Chương trình này được phát hành với điều kiện" + +#: lib/common.c:116 +msgid "of the GNU General Public License version 2." +msgstr "của Quyền Công Chung GNU (GPL) phiên bản 2." + +#: lib/common.c:118 +msgid "See the included COPYING file for details." +msgstr "Để tìm chi tiết thi hãy xem tập tin COPYING (chép) đã gồm." + +#: lib/common.c:175 +msgid "Connect options" +msgstr "Thiết lập kết nối" + +#: lib/common.c:183 +msgid "Enter IP address of your Access Point." +msgstr "Nhập địa chỉ IP của Điểm Truy cập bạn." + +#: lib/common.c:192 +msgid "Entered characters will not be displayed for security reason." +msgstr "Sẽ không hiển thị ký tự đã nhập, vì lý do bảo mật" + +#: lib/common.c:266 +msgid "This label will be stored on HDD (independently on AP name!)." +msgstr "Sẽ cất giữ nhãn này vào đĩa cứng (không phu thuộc vào tên Điểm TC)." + +#: lib/common.c:361 +msgid "Trying to probe AP for MIB properties. Please wait..." +msgstr "Cố dò Điểm TC để tìm thuộc tính MIB. Hãy đời..." + +#: lib/common.c:390 +msgid "" +"Unable to determine AP MIB properties (no response from AP). Press any key." +msgstr "" +"Không thể quyết định thuộc tính MIB của Điểm TC (Điểm TC không trả lời). Hãy " +"bấm bất cứ phím nào." + +#: lib/file.c:205 +msgid "NUM IP ADDRESS MIB TYPE MIB EXT. LABEL" +msgstr "SỐ ĐỊA CHỈ IP KIỂU MIB PHẦN MIB NHÃN" + +#: lib/file.c:206 +msgid "Choose an AP to connect to" +msgstr "Hãy chọn Điểm TC cần kết nối đến nó." + +#: lib/file.c:212 +msgid "1-9,C: connect; N: new; D: delete; W: save; Q: quit; arrows: scroll" +msgstr "1-9,C: kết nối; N: mới; D: xoá bỏ; W: lưu; Q: thoát; mũi tên: cuộn" + +#: lib/file.c:268 +msgid "Connect to AP num:" +msgstr "Kết nối đến Điểm TC số :" + +#: lib/file.c:304 +msgid "Delete num:" +msgstr "Xoá bỏ số :" + +#: lib/file.c:372 +msgid "AP list file ~/.ap-config successfully written. Press any key." +msgstr "" +"Tập tin danh sách Điểm TC <~/.ap-config> đã được ghi thành công. Hãy bấm bất " +"cứ phím nào." + +#: lib/file.c:376 +msgid "Unable to write AP list file ~/.ap-config. Press any key." +msgstr "" +"Không thể ghi tập tin danh sách Điểm TC ~/.ap-config. Hãy bấm bất cứ phím " +"nào." + +#: lib/file.c:426 +msgid "Unable to write stations file. Press any key." +msgstr "Không thể ghi tập tin trạm. Hãy bấm bất cứ phím nào." + +#: lib/file.c:428 +msgid "Stations file succesfully written. Press any key." +msgstr "Tập tin trạm đã được ghi thành công. Hãy bấm bất cứ phím nào." + +#: lib/input.c:33 +msgid "Invalid value. Press any key to continue." +msgstr "Giá trị không hợp lệ. Hãy bấm bất cứ phím nào để tiếp tục." + +#: lib/input.c:34 +#, c-format +msgid "Value must be in range %u - %u. Press any key to continue." +msgstr "" +"Giá trị phải ở trong phạm vị %u - %u. Hãy bấm bất cứ phím nào để tiếp tục." + +#: lib/input.c:412 +msgid "Y - Yes; Any other key - No (it's safer to answer No)" +msgstr "Y - Có; bất cứ phím khác nào - Không (an toàn hơn để trả lời Không)." + +#: lib/oui.c:6056 +msgid "Unknown or Private" +msgstr "Lạ hay Riêng" + +#: lib/radio.c:26 +msgid "" +"[key] - power level; UIOP or LR - antenna; W - write config; Q - quit to menu" +msgstr "" +"[key] - mức độ năng lượng; UIOP hay LR - ăngten; W - ghi cấu hình; Q - thoát " +"vào trình đơn" + +#: lib/radio.c:28 +msgid "Antenna:" +msgstr "Ăngten:" + +#: lib/radio.c:29 +msgid "[L] Left:" +msgstr "[L] Trái:" + +#: lib/radio.c:30 +msgid "[R] Right:" +msgstr "[R] Phải:" + +#: lib/radio.c:120 +msgid "Radio Configuration" +msgstr "Cấu hình rađiô" + +#: lib/radio.c:121 +msgid "Output RF signal power level (CR31 register values)" +msgstr "Mức độ năng lượng tín hiệu (giá trị thanh ghi CR31)" + +#: lib/radio.c:123 +msgid "Key Channel Level" +msgstr "Mức độ kênh khoá" + +#: lib/radio.c:279 +msgid "" +"You can't disable both antennas; unable to save antenna-config. Press any " +"key." +msgstr "" +"Không thể vô hiệu hóa cả hai ăngten; không thể lưu cấu hình ăngten (antenna-" +"config). Hãy bấm bất cứ phím nào." + +#: lib/scr.c:168 +#, c-format +msgid "Current AP: %s Type: %s Ext: %s" +msgstr "Điểm TC hiện có : %s Kiểu : %s Phần: %s" + +#: lib/set_community.c:27 +msgid "Set community/password" +msgstr "Lập cộng đồng/mật khẩu" + +#: lib/set_community.c:28 +msgid "Key Access level" +msgstr "Mức độ truy cập khoá" + +#: lib/set_community.c:29 +msgid "Community/Password" +msgstr "Cộng đồng/mật khẩu" + +#: lib/set_community.c:30 +msgid "[U] User " +msgstr "[U] Người dùng" + +#: lib/set_community.c:31 +msgid "[A] Administrator " +msgstr "[A] Quản trị" + +#: lib/set_community.c:32 +msgid "[M] Manufacturer " +msgstr "[M] Hãng chế tạo" + +#: lib/set_community.c:33 +msgid "" +"[key] - set community/password; W - write config to AP; Q - quit to menu" +msgstr "" +"[key] - lập cộng đồng/mật khẩu; W - ghi cấu hình vào Điểm TC; Q - thoát vào " +"trình đơn" + +#: lib/stat.c:29 +msgid "Ethernet Statistics" +msgstr "Thống kê Ethernet" + +#: lib/stat.c:30 +msgid "Wireless Statistics" +msgstr "Thống kê vô tuyến" + +#: lib/stat.c:98 +msgid "EthRxStat packet error. Press any key." +msgstr "Lỗi gói tin EthRxStat. Hãy bấm bất cứ phím nào." + +#: lib/stat.c:111 +msgid "EthTxStat packet error. Press any key." +msgstr "Lỗi gói tin EthTxStat. Hãy bấm bất cứ phím nào." + +#: lib/stat.c:115 ../src/gnome-netstatus.glade.h:16 +msgid "Received:" +msgstr "Ðã nhận:" + +#: lib/stat.c:116 +msgid "Transmitted:" +msgstr "Đã gởi:" + +#: lib/stat.c:270 +msgid "WirelessStat packet error. Press any key." +msgstr "Lỗi gói tin WirelessStat. Hãy bấm bất cứ phím nào." + +#: lib/sysinfo.c:29 +msgid "System Description: " +msgstr "Mô tả hệ thống:" + +#: lib/sysinfo.c:30 gpe-conf-sysinfo.desktop.in.h:1 +msgid "System Info" +msgstr "Thông tin hệ thống" + +#: lib/sysinfo.c:120 +msgid "Device hardware/software/name info:" +msgstr "Thông tin phần cứng/phần thêm/tên thiết bị:" + +#: lib/sysinfo.c:154 +msgid "Product name:" +msgstr "Tên sản phẩm:" + +#: lib/sysinfo.c:158 +#, c-format +msgid "Product type: %u" +msgstr "Kiểu sảnh phẩm: %u" + +#: lib/sysinfo.c:161 +msgid "OEM name:" +msgstr "Tên hãng chế tạo thiết bị gốc:" + +#: lib/sysinfo.c:168 +#, c-format +msgid "Hardware revision: %u" +msgstr "Phiên bản phần cứng: %u" + +#: lib/sysinfo.c:176 +#, c-format +msgid "Info structure version: %u" +msgstr "Phiên bản cấu trúc thông tin: %u" + +#: lib/sysinfo.c:179 lib/sysinfo.c:355 +#, c-format +msgid "Manufacturer OUI: %02X %02X %02X (%s)" +msgstr "Hãng chế tạo OUI: %02X %02X %02X (%s)" + +#: lib/sysinfo.c:196 +#, c-format +msgid "Uptime: %u days, %02u:%02u:%02u hours:mins:secs" +msgstr "Thời gian chạy: %u ngày, %02u:%02u:%02u giờ :phút:giây" + +# Name: do not translate/ tên: đừng dịch +#: lib/sysinfo.c:284 +msgid "FHSS 2.4 GHz" +msgstr "FHSS 2.4 GHz" + +# Name: do not translate/ tên: đừng dịch +#: lib/sysinfo.c:284 +msgid "DSSS 2.4 GHz" +msgstr "DSSS 2.4 GHz" + +#: lib/sysinfo.c:284 +msgid "IR Baseband" +msgstr "Dải tần cơ sở hồng ngoại" + +#: lib/sysinfo.c:285 +msgid "Commercial range 0..40 C" +msgstr "Phạm vị thương mại 0º..40º C" + +#: lib/sysinfo.c:286 +msgid "Industrial range -30..70 C" +msgstr "Phạm vị cộng nghiệp -30º..70º C" + +#: lib/sysinfo.c:289 ../src/gui.c:1050 libexif/canon/mnote-canon-entry.c:97 +#: libexif/canon/mnote-canon-entry.c:134 +msgid "manual" +msgstr "thủ công" + +#: lib/sysinfo.c:289 +msgid "notsupported" +msgstr "không hỗ trơ" + +#: lib/sysinfo.c:289 +msgid "dynamic" +msgstr "động" + +#: lib/sysinfo.c:345 ../driverdialog.glade.h:5 src/interface.c:90 +msgid "Manufacturer:" +msgstr "Hãng chế tạo :" + +#: lib/sysinfo.c:350 +msgid "Manufacturer ID:" +msgstr "ID hãng chế tạo:" + +#: lib/sysinfo.c:361 +msgid "Product Name:" +msgstr "Tên sản phẩm:" + +#: lib/sysinfo.c:367 +msgid "Product ID:" +msgstr "ID sản phẩm:" + +#: lib/sysinfo.c:373 +msgid "Product Version:" +msgstr "Phiên bản sản phẩm:" + +#: lib/sysinfo.c:379 +#, c-format +msgid "PHYType: %s" +msgstr "Kiểu PHY: %s" + +#: lib/sysinfo.c:382 +#, c-format +msgid "Temperature: %s" +msgstr "Nhiệt độ : %s" + +#: lib/sysinfo.c:390 +#, c-format +msgid "Regulatory Domain: %s" +msgstr "Miền điều tiết: %s" + +#: lib/sysinfo.c:391 +msgid "FCC (USA)" +msgstr "FCC (Mỹ)" + +#: lib/sysinfo.c:392 +msgid "DOC (Canada)" +msgstr "DOC (Ca-na-đa)" + +#: lib/sysinfo.c:393 +msgid "ETSI (Europe)" +msgstr "ETSI (Châu Âu)" + +#: lib/sysinfo.c:396 +msgid "MKK (Japan)" +msgstr "MKK (Nhật bản)" + +#: lib/sysinfo.c:399 +#, c-format +msgid "Transmit Power: %u mW" +msgstr "Năng lượng gởi: %u mW" + +#: lib/sysinfo.c:429 +#, c-format +msgid "WEP implemented: %s" +msgstr "Đã thực hiện WEP: %s" + +#: lib/sysinfo.c:432 +#, c-format +msgid "Diversity: %s" +msgstr "Độ nhiều dạng: %s" + +#: lib/sysinfo.c:460 +#, c-format +msgid "Uptime: %u:%02u:%02u.%02u" +msgstr "Thời gian chạy: %u:%02u:%02u.%02u" + +#: lib/sysinfo.c:480 +#, c-format +msgid "IP Address: %s" +msgstr "Địa chỉ IP: %s" + +#: lib/test.c:26 +msgid "[T] Test mode: " +msgstr "[T] Chế độ thử :" + +#: lib/test.c:27 +msgid "[A] Antenna: " +msgstr "[A] Ăngtên:" + +#: lib/test.c:28 +msgid "[S] Signal level: " +msgstr "[S] Mức độ tín hiệu :" + +#: lib/test.c:29 +msgid "[R] Rate: " +msgstr "[R] Tỷ lệ:" + +#: lib/test.c:30 +msgid "[F] TxFiler: " +msgstr "[F] TxFiler:" + +#: lib/test.c:31 +msgid "[O] Command: " +msgstr "[O] Lệnh:" + +#: lib/test.c:32 +msgid "T - Test mode On/Off; CASRFO - set options; Q - quit to menu" +msgstr "T — Bật/tắt chế độ thử; CASRFO — lập tùy chọn; Q — thoát vào trình đơn" + +#: src/fe-gtk/setup.c:190 ../plug-ins/gfig/gfig-dialog.c:1714 +#: ../lib/properties.c:64 ../lib/properties.h:468 ../lib/widgets.c:638 +#: ../glade/editor.c:508 ../glade/gbwidgets/gbtextview.c:49 +#: ../widgets/gtk+.xml.in.h:113 app/sample-editor.c:1461 +msgid "Left" +msgstr "Trái" + +#: src/fe-gtk/setup.c:191 ../plug-ins/gfig/gfig-dialog.c:1713 +#: ../lib/properties.c:66 ../lib/properties.h:470 ../lib/widgets.c:650 +#: ../glade/editor.c:517 ../glade/gbwidgets/gbtextview.c:50 +#: ../widgets/gtk+.xml.in.h:154 app/sample-editor.c:1473 +msgid "Right" +msgstr "Phải" + +#: lib/test.c:86 +msgid "Test mode" +msgstr "Chế độ thử" + +#: lib/test.c:89 +msgid "Using the \"Test mode\" may cause loss of your current" +msgstr "Dùng « Chế độ thử » có lẽ sẽ làm cho cấu hình hiện có" + +#: lib/test.c:90 +msgid "configuration." +msgstr "cấu hình." + +#: lib/test.c:102 ../src/nautilus-cvs.c:581 +msgid "Options:" +msgstr "Tùy chọn:" + +#: lib/test.c:128 +msgid "Statistics:" +msgstr "Thống kê:" + +#: lib/test.c:129 +msgid "Success Frames: 0 Failed Frames: 0" +msgstr "Khung được: 0 Khung không được: 0" + +#: lib/test.c:190 +#, c-format +msgid "Success Frames: %lu Failed Frames: %lu" +msgstr "Khung được: %lu Khung không được: %lu" + +#: lib/wep.c:28 +msgid "Privacy Settings" +msgstr "Thiết lập riêng tư" + +#: lib/wep.c:29 +msgid "[E] Standard encryption mechanism: " +msgstr "[E] Cơ chế mật mã chuẩn:" + +#: lib/wep.c:30 +msgid "[A] Allow unencrypted: " +msgstr "[A} Cho phép không mật mã:" + +#: lib/wep.c:31 +msgid "[K] Default WEP key: " +msgstr "[K] Khoá WEP mặc định:" + +#: lib/wep.c:32 +msgid "[P] Public key: " +msgstr "[P] Khoá công:" + +#: lib/wep.c:85 +msgid "EK1234 - set; W - write conf; Q - quit to menu" +msgstr "EK1234 - lập; W - ghi cấu hình; Q - thoát vào trình đơn" + +#: lib/wep.c:94 lib/wep.c:263 +msgid "Key WEP" +msgstr "Khoá WEP" + +#: lib/wep.c:101 +msgid "Hint! Confused by WEP key values? See man ap-config for info..." +msgstr "" +"Gợi ý! Bạn có lẫn lộn giữa nhưng giá trị khoá WEP khác nhau không? Hãy dùng " +"lệnh:\n" +"man ap-config" + +#: lib/wep.c:247 +msgid "AEPK1234 - set options; W - write conf; Q - quit to menu" +msgstr "AEPK1234 - lập tùy chọn; W - ghi cấu hình; Q - thoát vào trình đơn" + +# Name: do not translate/ tên: đừng dịch +#: lib/wlan.c:27 +msgid "[E] ESSID: " +msgstr "[E] ESSID: " + +#: lib/wlan.c:28 +msgid "[N] AP name: " +msgstr "[N] Tên Điểm TC: " + +#: lib/wlan.c:30 +msgid "[K] AP contact: " +msgstr "[K] Liên lạc Điểm TC: " + +#: lib/wlan.c:31 +msgid "[L] AP location: " +msgstr "[L] vị trí Điểm TC: " + +#: lib/wlan.c:33 +msgid "[R] RTS threshold: " +msgstr "[R] ngưỡng RTS: " + +#: lib/wlan.c:34 +msgid "[F] Fragmentation threshold: " +msgstr "[F] ngưỡng tế phân:" + +#: lib/wlan.c:35 +msgid "[P] Preambule type: " +msgstr "[P] Kiểu lời mở đầu :" + +#: lib/wlan.c:36 +msgid "[A] Auth type: " +msgstr "[A] Kiểu xác thực:" + +#: lib/wlan.c:37 +msgid "Open system" +msgstr "Hệ thống mở" + +#: lib/wlan.c:38 +msgid "Shared key" +msgstr "Khoá dùng chung" + +#: lib/wlan.c:39 +msgid "Both types" +msgstr "Cả hai kiểu" + +#: lib/wlan.c:40 +msgid "[U] Auto rate fallback: " +msgstr "[U] rút lui tỷ lệ tự động:" + +#: lib/wlan.c:41 +msgid "[S] Insert ESSID in broadcast packets: " +msgstr "[S] Chèn ESSID vào gói tin phát thanh:" + +#: lib/wlan.c:42 +msgid "Basic and Supported rates:" +msgstr "Tỷ lệ cơ ban và đã hỗ trợ :" + +#: lib/wlan.c:43 +msgid "Key Rate Status" +msgstr "Khoá Tỷ lệ Trang thái" + +#: lib/wlan.c:45 +msgid "[I] International roaming: " +msgstr "[I] đi lang thang khắp thế giới:" + +#: lib/wlan.c:46 +msgid "[B] Beacon period (msec): " +msgstr "[B] chu kỳ máy tín hiệu (miligiây):" + +#: lib/wlan.c:47 +msgid "[D] DTIM sending interval (beacons): " +msgstr "[D] thời gian giữa lần gởi DTIM (máy tín hiệu)" + +#: lib/wlan.c:48 +msgid "[T] SIFS time (msec): " +msgstr "[T] Thời gian SIFS (miligiây):" + +#: lib/wlan.c:49 +msgid "[key] - set option; W - write conf; Q - quit to menu" +msgstr "[key] - lập tùy chọn; W - ghi cấu hình; Q - thoát vào trình đơn" + +#: lib/wlan.c:125 libexif/exif-format.c:35 +msgid "Short" +msgstr "Ngắn" + +#: lib/wlan.c:125 libexif/exif-format.c:36 +#, fuzzy +msgid "Long" +msgstr "" +"#-#-#-#-# Compendium04.po (NAME) #-#-#-#-#\n" +"Lâu\n" +"#-#-#-#-# libexif-0.6.13.vi.po (libexif-0.6.13) #-#-#-#-#\n" +"Dài" + +#: lib/wlan.c:237 +msgid "Wireless Settings" +msgstr "Thiết lập vô tuyến" + +#: lib/wlan.c:731 +msgid "Antenna Configuration:" +msgstr "Cấu hình ăngten:" + +#: ../plug-ins/MapObject/mapobject_ui.c:473 +msgid "General Options" +msgstr "Tùy chọn chung" + +#: lib/wlan.c:750 +msgid "" +"UIOPTY - antenna; SCANLEDFR1234 - options; W - write conf; Q - quit to menu" +msgstr "" +"UIOPTY - ăngten; SCANLEDFR1234 - tùy chọn; W - ghi cấu hình; Q - thoát vào " +"trình đơn" + +#: src/ap-config.c:54 +msgid "Set general options" +msgstr "Lập tùy chọn chung" + +#: src/ap-config.c:55 +msgid "Set advanced options" +msgstr "Lập tùy chọn nâng cao" + +#: src/ap-config.c:89 +msgid "Reset AP." +msgstr "Lập lại Điểm TC" + +#: src/ap-config.c:122 +msgid "Latest" +msgstr "Mới nhất" + +#: src/ap-config.c:122 +msgid "Get info about latest events" +msgstr "Gọi tin tức về sự kiện mới nhất" + +#: src/ap-mrtg.c:42 +msgid "" +"\n" +"Usage:\n" +msgstr "" +"\n" +"Cách sử dụng:\n" + +#: src/ap-mrtg.c:44 +msgid "" +"\tap-mrtg -i ip -c community -t type [-b bssid] [-n name] [-a aptype] [-v] [-" +"h] [-r]\n" +"\n" +msgstr "" +"\tap-mrtg -i ip -c cộng đồng -t kiểu [-b bssid] [-n tên] [-a kiểu Điểm TC] [-" +"v] [-h] [-r]\n" +"\n" + +#: src/ap-mrtg.c:46 +msgid "" +"Get stats from AP and return it in MRTG parsable format\n" +"\n" +msgstr "" +"Gọi thống kê từ Điểm TC và gởi trả nó trong dạng mà MRTG có phân tách được\n" + +#: src/ap-mrtg.c:47 +msgid "-i ip - AP ip address\n" +msgstr "-i ip - địa chỉ IP của Điểm TC\n" + +#: src/ap-mrtg.c:48 +msgid "-c community - SNMP community string\n" +msgstr "-c community - chuỗi _cộng đồng_ SNMP\n" + +#: src/ap-mrtg.c:50 +msgid "" +"-t type - statistics type ireless, thernet, associated tations " +"or ink quality in client mode\n" +msgstr "" +"-t type - _kiểu_ thống kê:\n" +"w - vô tuyến\n" +" e - Ethernet\n" +"s - trạm liên quan\n" +" l - chất lượng liên kết trong chế độ máy khách\n" + +#: src/ap-mrtg.c:52 +msgid "" +"-b bssid - mac address of the AP to which get link quality, only if " +"type=l\n" +msgstr "" +"-b bssid - địa chỉ MAC của Điểm TC mà cần goi chất lượng liên kết đến " +"nó, chỉ nếu kiểu=l\n" + +#: src/ap-mrtg.c:53 +msgid "-n name - AP name - for check only\n" +msgstr "-n name - _tên_ Điểm TC - chỉ để kiểm tra\n" + +#: src/ap-mrtg.c:54 +msgid "" +"-a aptype - AP type - 410 (default) or 510 for ATMEL12350's, like the " +"ME-102\n" +msgstr "" +"-a aptype - _kiểu Điểm TC_ - 410 (mặc định) hay 510 cho các máy " +"ATMEL12350, nhưME-102\n" + +#: src/ap-mrtg.c:56 +msgid "-v - report MRTG about problems connecting to AP\n" +msgstr "-v - thông báo MRTG về vấn đề khi kết nối đến Điểm TC\n" + +#: src/ap-mrtg.c:57 +msgid "-r - reset AP when getting LinkQuality stats\n" +msgstr "" +"-r - _lập lại_ Điểm TC khi gọi thống kê chất lượng liên kết\n" + +#: src/ap-mrtg.c:58 +msgid "" +"-h - print this help screen\n" +"\n" +msgstr "" +"-h - hiển thị _trợ giúp_ nàỳ\n" +"\n" + +#: src/ap-mrtg.c:59 +#, c-format +msgid "" +"ap-mrtg %s Copyright (c) 2002-2003 Roman Festchook\n" +"\n" +msgstr "" +"ap-mrtg %s Bản quyền © năm 2002-2003 Roman Festchook\n" +"\n" + +#: src/ap-mrtg.c:143 +msgid "Invalid IP-address\n" +msgstr "Địa chỉ IP không hợp lệ\n" + +#: src/ap-mrtg.c:188 +#, c-format +msgid "Invalid AP-Type '%s' - valid types are 510 or 410\n" +msgstr "Kiểu Điểm TC không hợp lệ '%s' - kiểu hợp lệ là 510 hay 410\n" + +#: src/ap-mrtg.c:207 +msgid "Create socket error" +msgstr "Lỗi tạo ổ cắm" + +#: src/ap-mrtg.c:211 +msgid "Bind socket error" +msgstr "Lỗi đóng kết ổ cắm" + +#: src/ap-trapd.c:148 +#, c-format +msgid "ap-trapd %s started%s%s." +msgstr "ap-trapd %s đã khởi chạy%s%s." + +#: src/ap-trapd.c:149 +msgid " on " +msgstr " bật" + +#: src/ap-trapd.c:155 +msgid "Unable to fork. Exiting." +msgstr "Không thể tạo tiến trình con nên thoát." + +#: src/ap-trapd.c:159 +msgid "Can't create socket. Exiting." +msgstr "Không tạo ổ cắm nên thoát." + +#: src/ap-trapd.c:165 +msgid "Can't bind socket. Exiting." +msgstr "Không thể đóng kết ổ cắm nên thoát." + +#: src/ap-trapd.c:172 +#, c-format +msgid "Can't bind to device %s. Exiting." +msgstr "Không thể đóng kết thiết bị %s nên thoát." + +#: src/ap-trapd.c:183 +#, c-format +msgid "Unable to process username %s. Error: %m." +msgstr "Không thể xử lý tên người dùng %s. Lỗi: %m." + +#: src/ap-trapd.c:188 +#, c-format +msgid "Unable to change to uid %d." +msgstr "Không thể chuyển đổi sang UID %d." + +#: src/ap-trapd.c:235 +#, c-format +msgid "" +"Received unknown SNMP ver %d trap. From %s:%d. Agent: %s. Community: %s." +msgstr "" +"Đã nhận sự bắt SNMP phiên bản %d lạ. Từ %s:%d. Tác nhân: %s. Cộng đồng: %s." + +#: src/ap-trapd.c:307 +#, c-format +msgid "Agent:v%d %s (%s@%s:%d) %s%s%s. SysUptime %d:%02d:%02d.%02d" +msgstr "" +"Tác nhân:v%d %s (%s@%s:%d) %s%s%s. Thời gian chạy hệ thống %d:%02d:%02d.%02d" + +#: src/auth_mac.c:33 +msgid "NUM MAC address" +msgstr "SỐ Địa chỉ MAC" + +#: src/bridge.c:35 +msgid "[C] Configuration-enabled port(s): " +msgstr "[C] Cổng đã bật trong cấu hình:" + +#: src/bridge.c:38 +msgid "[B] Isolate wireless clients (broadcast traffic): " +msgstr "[B] Cách máy khách vô tuyến (tải phát thanh):" + +#: src/bridge.c:39 +msgid "[U] Isolate wireless clients (unicast traffic): " +msgstr "[U] Cách máy khách vô tuyến (tải một-một):" + +#: src/nwn_advanced.c:60 +#, c-format +msgid "[D] DB Station Timeout: %d" +msgstr "[D] Thời hạn trạm DB: %d" + +#: src/nwn_advanced.c:62 +#, c-format +msgid "[A] ACK Window: %d" +msgstr "[A] Cửa sổ ACK: %d" + +#: pppconfig:323 ../plug-ins/common/warp.c:552 +msgid "Advanced Options" +msgstr "Tùy chọn cấp cao" + +#: src/nwn_advanced.c:66 +msgid "DA - options; W - write conf; Q - quit to menu" +msgstr "DA - tùy chọn; W - ghi cấu hình Q - thoát vào trình đơn" + +#: src/nwn_latest.c:30 +#, c-format +msgid "Reason: %u Station: %02X%02X%02X%02X%02X%02X" +msgstr "Lý do: %u Trạm: %02X%02X%02X%02X%02X%02X" + +#: src/nwn_latest.c:88 +msgid "Latest Events" +msgstr "Sự kiện mới nhất" + +#: src/nwn_latest.c:89 +msgid "Disassociate:" +msgstr "Phân ra:" + +#: src/nwn_latest.c:96 +msgid "Deauthenticate:" +msgstr "Bỏ xác thực:" + +#: src/nwn_latest.c:103 +msgid "Authenticate Fail:" +msgstr "Không xác thực được:" + +#: src/nwn_latest.c:109 ../app/dialogs/module-dialog.c:506 +msgid "Last error:" +msgstr "Lỗi cuối cùng:" + +#: src/nwn_latest.c:110 +msgid "Error:" +msgstr "Lỗi:" + +#: src/stations.c:33 +msgid "AP link state" +msgstr "Tính trạng liên kết Điểm TC" + +# Name: do not translate/ tên: đừng dịch +#: src/stations.c:37 +msgid " # MAC " +msgstr " # MAC " + +#: src/stations.c:40 +msgid " # MAC Parent MAC RSSI Status MACn IP " +msgstr " # MAC MAC cha RSSI Trạngt MACn IP " + +#: src/stations.c:195 src/stations.c:367 +msgid "" +"Arrows - scroll; S - save to file; Q - return; T - toggle view; Other - " +"refresh" +msgstr "" +"Mũi tên - cuộn; S - lưu vào tập tin; Q - trở về; T - bật/tắt khung xem; Khác " +"- cập nhật" + +#: src/stations.c:200 +msgid "Arrows - scroll; S - save to file; Q - return; Other key - refresh" +msgstr "Mũi tên - cuộn; S - lưu vào tập tin; Q - trở về; Phím khác - cập nhật" + +#: src/stations.c:279 +msgid "Id MAC address Quality Age RSSI" +msgstr "Id Địa chỉ MAC Chất lượng Cũ RSSI" + +#: common/info.cpp:232 +msgid "a number between 0 and 1" +msgstr "một số giữa 0 và 1" + +#: common/info.cpp:569 +msgid "in the form \" \"" +msgstr "kiểu \" \"" + +#. TRANSLATORS: "true" and "false" are literal +#. * values and should not be translated. +#: common/config.cpp:977 +msgid "either \"true\" or \"false\"" +msgstr "hoặc \"true\" (thật) hoặc \"false\" (không thật)" + +#: common/config.cpp:996 +msgid "a positive integer" +msgstr "số nguyên dương" + +#: common/config.cpp:1124 +msgid "# default: " +msgstr "# mặc định: " + +#: common/config.cpp:1187 +#, c-format +msgid "" +"\n" +"#######################################################################\n" +"#\n" +"# Filter: %s\n" +"# %s\n" +"#\n" +"# configured as follows:\n" +"\n" +msgstr "" +"\n" +"#######################################################################\n" +"#\n" +"# Bộ lọc: %s\n" +"# %s\n" +"#\n" +"# có cấu hình như theo đây:\n" +"\n" + +#: common/config.cpp:1285 +msgid "ASPELL_CONF env var" +msgstr "ASPELL_CONF env var (biến môi trường cấu hình trình Aspell)" + +#: common/config.cpp:1359 +msgid "main configuration file" +msgstr "tâp tin cấu hình chính" + +#: common/config.cpp:1361 +msgid "location of main configuration file" +msgstr "vị trí của tập tin cấu hình chính" + +#: common/config.cpp:1364 +msgid "location of language data files" +msgstr "vị trí của tập tin dữ liệụ ngôn ngữ" + +#: common/config.cpp:1366 +msgid "create dictionary aliases" +msgstr "tạo biệt hiệu từ điển" + +#: common/config.cpp:1368 +msgid "location of the main word list" +msgstr "vị trí danh sách từ chính" + +#: common/config.cpp:1370 +msgid "encoding to expect data to be in" +msgstr "ngờ dư liệu bằng mã hóa này" + +#: common/config.cpp:1372 +msgid "add or removes a filter" +msgstr "thêm hay bỏ bộ lọc" + +#: common/config.cpp:1374 +msgid "path(s) aspell looks for filters" +msgstr "trinh aspell tìm bộ lọc theo đường dẫn này" + +#: common/config.cpp:1378 +msgid "filter mode" +msgstr "chế độ lọc" + +#: common/config.cpp:1380 +msgid "extra dictionaries to use" +msgstr "từ điển thêm để sử dụng" + +#: common/config.cpp:1382 +msgid "location for personal files" +msgstr "vị trí của tập tin cá nhân" + +#: common/config.cpp:1384 +msgid "ignore words <= n chars" +msgstr "bỏ qua từ <= n ký tự" + +#. TRANSLATORS: It is OK if this is longer than 50 chars +#: common/config.cpp:1387 +msgid "ignore accents when checking words -- CURRENTLY IGNORED" +msgstr "bỏ qua dấu khi kiểm tra từ -- HIỆN BỎ QUA" + +#: common/config.cpp:1389 +msgid "ignore case when checking words" +msgstr "bỏ qua hoa/thường khi kiểm tra từ" + +#: common/config.cpp:1391 +msgid "ignore commands to store replacement pairs" +msgstr "bỏ qua lệnh để cất giữ đôi từ thay thế" + +#: common/config.cpp:1393 common/config.cpp:1460 +msgid "extra information for the word list" +msgstr "thông tin thêm cho danh sách từ" + +#: common/config.cpp:1395 +msgid "keyboard definition to use for typo analysis" +msgstr "cấu hình bàn phím để sử dụng để phân tích lỗi đánh máy" + +#: common/config.cpp:1397 +msgid "language code" +msgstr "mã ngôn ngữ (Việt ngữ là vi)" + +#: common/config.cpp:1399 +msgid "deprecated, use lang instead" +msgstr "bị phân đối nên hãy sử dụng đối số lang thay thế" + +#: common/config.cpp:1401 +msgid "location of local language data files" +msgstr "vị trí của tập tin dữ liệu ngôn ngữ địa phương" + +#: common/config.cpp:1403 +msgid "base name of the main dictionary to use" +msgstr "tên cơ sở của từ điển chính để sử dụng" + +#: common/config.cpp:1407 +msgid "set module name" +msgstr "lập tên mô-đun" + +#: common/config.cpp:1409 +msgid "search order for modules" +msgstr "thứ tự tìm kiếm mô-đun" + +#: common/config.cpp:1411 +msgid "enable Unicode normalization" +msgstr "hiệu lực việc tiêu chuẩn hóa Unicode (Chỉ môt mã)" + +#: common/config.cpp:1413 +msgid "Unicode normalization required for current lang" +msgstr "Ngôn ngữ hiện cần đến việc tiêu chuẩn hóa Unicode" + +#. TRANSLATORS: the values after the ':' are literal +#. values and should not be translated. +#: common/config.cpp:1417 +msgid "Unicode normalization form: none, nfd, nfc, comp" +msgstr "kiểu tiêu chuẩn hóa Unicode: none (không có), nfd, nfc, comp" + +#: common/config.cpp:1419 +msgid "avoid lossy conversions when normalization" +msgstr "tránh việc chuyển đổi thiếu gì khi tiêu chuẩn hóa" + +#: common/config.cpp:1421 +msgid "personal configuration file" +msgstr "tập tin cấu hình cá nhân" + +#: common/config.cpp:1424 +msgid "personal dictionary file name" +msgstr "tên tập tin từ điển cá nhân" + +#: common/config.cpp:1427 +msgid "prefix directory" +msgstr "thư mục tiền tố" + +#: common/config.cpp:1429 +msgid "replacements list file name" +msgstr "tên tập tin danh sách từ thay thế" + +#: common/config.cpp:1432 +msgid "consider run-together words legal" +msgstr "cho phép từ được kết hợp" + +#: common/config.cpp:1434 +msgid "maximum number that can be strung together" +msgstr "tối đa số từ có thể kết hợp nhau" + +#: common/config.cpp:1436 +msgid "minimal length of interior words" +msgstr "tốí thiểu độ dài từ nội bộ" + +#: common/config.cpp:1438 +msgid "save replacement pairs on save all" +msgstr "lưu đôi từ thay thế khi Lưu tất cả" + +#: common/config.cpp:1440 +msgid "set the prefix based on executable location" +msgstr "lập tiền tố trên cơ sở vị trí trình chạy" + +#: common/config.cpp:1442 +msgid "size of the word list" +msgstr "cỡ danh sách từ" + +#: common/config.cpp:1444 +msgid "no longer used" +msgstr "không còn sử dụng lại" + +#: common/config.cpp:1446 +msgid "suggestion mode" +msgstr "chế độ góp ý" + +#. TRANSLATORS: "sug-mode" is a literal value and should not be +#. translated. +#: common/config.cpp:1450 +msgid "edit distance to use, override sug-mode default" +msgstr "" +"hiệu chỉnh tầm để sử dụng, có quyền cao hơn sug-mode (chế độ góp ý) mặc định" + +#: common/config.cpp:1452 +msgid "use typo analysis, override sug-mode default" +msgstr "" +"phân tích lỗi đánh máy, co quyền cao hơn sug-mode (chế độ góp ý) mặc định" + +#: common/config.cpp:1454 +msgid "use replacement tables, override sug-mode default" +msgstr "" +"sử dụng bảng thay thế, có quyền cao hơn sug-mode (chế độ góp ý) măc định" + +#: common/config.cpp:1456 +msgid "characters to insert when a word is split" +msgstr "ký tự để chèn khi chia tách từ" + +#: common/config.cpp:1458 +msgid "use personal, replacement & session dictionaries" +msgstr "sử dụng các từ điển loại cá nhân, thay thế và phiên hợp" + +#: common/config.cpp:1462 +msgid "search path for word list information files" +msgstr "đường dẫn tìm kiếm đốí với tập tin thông tin danh sách từ" + +#: common/config.cpp:1464 +msgid "enable warnings" +msgstr "hiệu lực lời cảnh báo" + +#. TRANSLATORS: It is OK if this is longer than 50 chars +#: common/config.cpp:1474 +msgid "indicator for affix flags in word lists -- CURRENTLY IGNORED" +msgstr "cái chỉ cờ phụ tố trong danh sách từ -- HIỆN BỎ QUA" + +#: common/config.cpp:1476 +msgid "use affix compression when creating dictionaries" +msgstr "sử dụng cách nén loại affix (thêm vào) khi tạo từ điển" + +#: common/config.cpp:1478 +msgid "remove invalid affix flags" +msgstr "bỏ cờ affix (thêm vào) không hợp lệ" + +#: common/config.cpp:1480 +msgid "attempts to clean words so that they are valid" +msgstr "lần cố sửa từ để làm hợp lệ" + +#: common/config.cpp:1482 +msgid "compute soundslike on demand rather than storing" +msgstr "tính soundslike (nghe như) khi lệnh, không cất giữ" + +#: common/config.cpp:1484 +msgid "partially expand affixes for better suggestions" +msgstr "mở rộng affix (thêm vào) cục bộ để góp ý tốt hơn" + +#: common/config.cpp:1486 +msgid "skip invalid words" +msgstr "bỏ qua từ không hợp lệ" + +#: common/config.cpp:1488 +msgid "check if affix flags are valid" +msgstr "kiểm tra cờ affix (thêm vào) là hợp lệ" + +#: common/config.cpp:1490 +msgid "check if words are valid" +msgstr "kiểm tra từ la hợp lệ" + +#: common/config.cpp:1497 +msgid "create a backup file by appending \".bak\"" +msgstr "tạo tập tin lưu trữ bằng cách thêm vào phần cuối \".bak\"" + +#: common/config.cpp:1499 +msgid "use byte offsets instead of character offsets" +msgstr "sử dụng hiệu số byte thay thế hiệu số ký tự" + +#: common/config.cpp:1501 +msgid "create missing root/affix combinations" +msgstr "tạo sự kết hợp gốc/phụ tố thiếu" + +#: common/config.cpp:1503 +msgid "keymapping for check mode: \"aspell\" or \"ispell\"" +msgstr "ảnh xạ khoá đốí với chế độ kiểm tra: \"aspell\" hay \"ispell\"" + +#: common/config.cpp:1505 +msgid "reverse the order of the suggest list" +msgstr "đổi chiếu thứ tự danh sách góp ý" + +#: common/config.cpp:1507 +msgid "suggest possible replacements" +msgstr "góp ý từ thay thế co thể" + +#: common/config.cpp:1509 +msgid "time load time and suggest time in pipe mode" +msgstr "ghi thời gian tải và thời gian góp ý khi trong chế độ ống" + +#: common/convert.cpp:303 common/convert.cpp:429 +#, c-format +msgid "" +"This could also mean that the file \"%s\" could not be opened for reading or " +"does not exist." +msgstr "" +"Cũng có thể nghĩa là không thể mở tập tin \"%s\" để đọc, hay tập tin đó " +"không tồn tại." + +#: common/convert.cpp:552 common/convert.cpp:659 common/convert.cpp:705 +#, c-format +msgid "The Unicode code point U+%04X is unsupported." +msgstr "Không hỗ trợ điểm mã Unicode U+%04X." + +#: common/convert.cpp:829 +#, c-format +msgid "Invalid UTF-8 sequence at position %d." +msgstr "Dãy UTF-8 không hợp lệ tại vị trí %d." + +#: common/errors.cpp:27 +msgid "Operation Not Supported: %what:1" +msgstr "Không hỗ trợ thi hành: %what:1." + +#: common/errors.cpp:43 +msgid "The method \"%what:1\" is unimplemented in \"%where:2\"." +msgstr "Không thi hành phương pháp « %what:1 » trong « %where:2 »." + +#: common/errors.cpp:51 +#, c-format +msgid "%file:1:" +msgstr "%file:1:" + +#: common/errors.cpp:59 +#, c-format +msgid "The file \"%file:1\" can not be opened" +msgstr "Không thể mở tập tin « %file:1 »." + +#: common/errors.cpp:67 +#, c-format +msgid "The file \"%file:1\" can not be opened for reading." +msgstr "Không thể mở tập tin « %file:1 » để đọc." + +#: common/errors.cpp:75 +#, c-format +msgid "The file \"%file:1\" can not be opened for writing." +msgstr "Không thể mở tập tin « %file:1 » để ghi." + +#: common/errors.cpp:83 +#, c-format +msgid "The file name \"%file:1\" is invalid." +msgstr "Tên tập tin « %file:1 » không hợp lệ." + +#: common/errors.cpp:91 +#, c-format +msgid "The file \"%file:1\" is not in the proper format." +msgstr "Tập tin « %file:1 » không có dạng đúng." + +#: common/errors.cpp:107 +#, c-format +msgid "The directory \"%dir:1\" can not be opened for reading." +msgstr "Không thể mở thư mục « %dir:1 » để đọc." + +#: common/errors.cpp:123 +msgid "The key \"%key:1\" is unknown." +msgstr "Không biết khoá « %key:1 »." + +#: common/errors.cpp:131 +msgid "The value for option \"%key:1\" can not be changed." +msgstr "Không thay đổi được trị số cho tùy chọn « %key:1 »." + +#: common/errors.cpp:139 +msgid "The key \"%key:1\" is not %accepted:2 and is thus invalid." +msgstr "Khoá « %key:1 » không %accepted:2 thì không hợp lệ." + +#: common/errors.cpp:147 +msgid "" +"The value \"%value:2\" is not %accepted:3 and is thus invalid for the key \"%" +"key:1\"." +msgstr "" +"Trị số « %value:2 » không %accepted:3 thì không hợp lệ đối với khoá « %key:1 " +"»." + +#: common/errors.cpp:163 +msgid "The key \"%key:1\" is not a string." +msgstr "Khoá « %key:1 » không là chuỗi." + +#: common/errors.cpp:171 +msgid "The key \"%key:1\" is not an integer." +msgstr "Khoá « %key:1 » không là số nguyên." + +#: common/errors.cpp:179 +msgid "The key \"%key:1\" is not a boolean." +msgstr "Khoá « %key:1 » không là bun." + +#: common/errors.cpp:187 +msgid "The key \"%key:1\" is not a list." +msgstr "Khoá « %key:1 » không là danh sách." + +#: common/errors.cpp:195 +msgid "" +"The key \"%key:1\" does not take any parameters when prefixed by a \"reset-" +"\"." +msgstr "" +"Khoá « %key:1 » không nhận tham số khi có tiền tố « reset- » (lập lại)." + +#: common/errors.cpp:203 +msgid "" +"The key \"%key:1\" does not take any parameters when prefixed by a \"enable-" +"\"." +msgstr "" +"Khoá « %key:1 » không nhận tham số khi có tiền tố « enable- » (hiệu lực)." + +#: common/errors.cpp:211 +msgid "" +"The key \"%key:1\" does not take any parameters when prefixed by a \"dont-\" " +"or \"disable-\"." +msgstr "" +"Khoá « %key:1 » không nhận tham số khi có tiền tố « dont- » hay « disable- " +"» (không hay vô hiệu hóa)." + +#: common/errors.cpp:219 +msgid "" +"The key \"%key:1\" does not take any parameters when prefixed by a \"clear-" +"\"." +msgstr "Khoá « %key:1 » không nhận tham số khi có tiền tố « clear- » (xoá)." + +#: common/errors.cpp:235 +#, c-format +msgid "The language \"%lang:1\" is not known." +msgstr "Không biết ngôn ngữ « %lang:1 »." + +#: common/errors.cpp:243 +#, c-format +msgid "The soundslike \"%sl:2\" is not known." +msgstr "Không biết điều soundslike (nghe như) « %sl:2 »." + +#: common/errors.cpp:251 +#, c-format +msgid "The language \"%lang:1\" is not supported." +msgstr "Không hỗ trợ ngôn ngữ « %lang:1 »." + +#: common/errors.cpp:259 +#, c-format +msgid "No word lists can be found for the language \"%lang:1\"." +msgstr "Không tìm được danh sách từ đối với ngôn ngữ « %lang:1 »." + +#: common/errors.cpp:267 +#, c-format +msgid "Expected language \"%lang:1\" but got \"%prev:2\"." +msgstr "Ngờ ngôn ngữ « %lang:1 » nhưng có « %prev:2 »." + +#: common/errors.cpp:283 +#, c-format +msgid "Affix '%aff:1' is corrupt." +msgstr "Phụ tố « %aff:1 » là hỏng." + +#: common/errors.cpp:291 +#, c-format +msgid "The condition \"%cond:1\" is invalid." +msgstr "Điều khiển « %cond:1 » không hợp lệ." + +#: common/errors.cpp:299 +#, c-format +msgid "" +"The condition \"%cond:1\" does not guarantee that \"%strip:2\" can always be " +"stripped." +msgstr "Điều khiển « %cond:1 » không bảo đảm luôn có thể tước « %strip:2 »." + +#: common/errors.cpp:307 +#, c-format +msgid "" +"The file \"%file:1\" is not in the proper format. Expected the file to be in " +"\"%exp:2\" not \"%got:3\"." +msgstr "" +"Tập tin « %file:1 » không co dạng đúng. Ngờ tập tin dạng « %exp:2 » không " +"phải « %got:3 »." + +#: common/errors.cpp:323 +#, c-format +msgid "The encoding \"%encod:1\" is not known." +msgstr "Không biết mã « %encod:1 »." + +#: common/errors.cpp:331 +#, c-format +msgid "The encoding \"%encod:1\" is not supported." +msgstr "Không hỗ trợ mã « %encod:1 »." + +#: common/errors.cpp:339 +#, c-format +msgid "The conversion from \"%encod:1\" to \"%encod2:2\" is not supported." +msgstr "Không hỗ trợ việc chuyển đổi từ « %encod:1 » sang « %encod2:2 »." + +#: common/errors.cpp:379 +#, c-format +msgid "The string \"%str:1\" is invalid." +msgstr "Chuỗi « %str:1 » không hợp lệ." + +#: common/errors.cpp:387 +msgid "The word \"%word:1\" is invalid." +msgstr "Từ « %word:1 » không hợp lệ." + +#: common/errors.cpp:395 +msgid "The affix flag '%aff:1' is invalid for word \"%word:2\"." +msgstr "Cờ affix (thêm vào) « %aff:1 » không hợp lệ đối với « %word:2 »." + +#: common/errors.cpp:403 +msgid "The affix flag '%aff:1' can not be applied to word \"%word:2\"." +msgstr "Không áp dụng cờ affix (thêm vào) '%aff:1' vào từ « %word:2 »." + +#: common/errors.cpp:451 +msgid "not a version number" +msgstr "không là số phiên bản" + +#: common/errors.cpp:467 +msgid "dlopen returned \"%return:1\"." +msgstr "lệnh dlopen đã gọi « %return:1 »." + +#: common/errors.cpp:475 +#, c-format +msgid "The file \"%filter:1\" does not contain any filters." +msgstr "Tập tin « %filter:1 » không có bộ lọc nào." + +#: common/errors.cpp:483 +#, c-format +msgid "The filter \"%filter:1\" does not exist." +msgstr "Không có bộ lọc « %filter:1 »." + +#: common/errors.cpp:491 common/errors.cpp:587 +msgid "Confused by version control." +msgstr "Điều khiển phiên bản bối rối." + +#: common/errors.cpp:499 +msgid "Aspell version does not match filter's requirement." +msgstr "Phiên bản trình Aspell không khớp điều mà bộ lọc cần đến." + +#: common/errors.cpp:507 +msgid "Filter option already exists." +msgstr "Tùy chọn lọc đó đã có." + +#: common/errors.cpp:515 +msgid "Use option modifiers only within named option." +msgstr "Hãy sử dụng cờ sửa đổi tùy chọn chỉ ở trong tùy chọn đó." + +#: common/errors.cpp:523 +msgid "Option modifier unknown." +msgstr "Không biết cờ sửa đổi tùy chọn đó." + +#: common/errors.cpp:531 +msgid "Error setting filter description." +msgstr "Gặp lỗi khi lập mô tả bộ lọc." + +#: common/errors.cpp:547 +msgid "Empty option specifier." +msgstr "Điều ghi rõ tùy chọn trống." + +#: common/errors.cpp:555 +#, c-format +msgid "Option \"%option:1\" possibly specified prior to filter." +msgstr "Có lẽ đã ghi rõ tùy chọn \"%option:1\" trước bộ lọc." + +#: common/errors.cpp:563 +msgid "Unknown mode description key \"%key:1\"." +msgstr "Không biết khoá diễn tả chế độ \"%key:1\"." + +#: common/errors.cpp:571 +#, c-format +msgid "Expecting \"%modekey:1\" key." +msgstr "Ngờ khoá \"%modekey:1\"." + +#: common/errors.cpp:579 +msgid "Version specifier missing key: \"aspell\"." +msgstr "Thiếu khoá \"aspell\" khi ghi rõ phiên bản." + +#: common/errors.cpp:595 +msgid "Aspell version does not match mode's requirement." +msgstr "Phiên bản trình Aspell không khớp điều mà chế độ cần đến." + +#: common/errors.cpp:603 +msgid "Missing magic mode expression." +msgstr "Thiếu biểu thức chế độ mã thuật." + +#: common/errors.cpp:611 +#, c-format +msgid "Empty extension at char %char:1." +msgstr "Điều mở rộng trống tại ký tự %char:1." + +#: common/errors.cpp:619 +#, c-format +msgid "\"%mode:1\" error" +msgstr "Lỗi \"%mode:1\"" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.mo b/vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.mo new file mode 100644 index 0000000000000000000000000000000000000000..c3e53faf9e34d7633448c9e25ab1a62aa4523c18 GIT binary patch literal 27348 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.po b/vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.po new file mode 100644 index 0000000000..7bdd3f7ac7 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/poedit-1.5.7-zh_CN.po @@ -0,0 +1,1591 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Poedit 1.5\n" +"Report-Msgid-Bugs-To: poedit@googlegroups.com\n" +"POT-Creation-Date: 2012-07-30 10:34+0200\n" +"PO-Revision-Date: 2013-02-24 21:00+0800\n" +"Last-Translator: Christopher Meng \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 1.5.5\n" + +#: ../src/edframe.cpp:2060 +msgid " (modified)" +msgstr " (已修改)" + +#. TRANSLATORS: This is version information in about dialog, it is followed +#. by version number when used (wxWidgets 2.8) +#: ../src/edframe.cpp:2431 +msgid " Version " +msgstr " 版本 " + +#: ../src/edframe.cpp:1367 +#, c-format +msgid "%d issue with the translation found." +msgid_plural "%d issues with the translation found." +msgstr[0] "在翻译中发现了 %d 个问题。" + +#: ../src/edframe.cpp:2024 +#, c-format +msgid "%i %% translated, %i string" +msgid_plural "%i %% translated, %i strings" +msgstr[0] "%i%% 已翻译,%i 个字串" + +#: ../src/edframe.cpp:2029 +#, c-format +msgid "%i %% translated, %i string (%s)" +msgid_plural "%i %% translated, %i strings (%s)" +msgstr[0] "%i%% 已翻译,%i 个字串 (%s)" + +#: ../src/export_html.cpp:134 +#, c-format +msgid "" +"%i %% translated, %i strings (%i fuzzy, %i bad tokens, %i not translated)" +msgstr "%i%% 已翻译,%i 个字串 (%i 个模糊翻译,%i 个错误的标记,%i 个未翻译)" + +#: ../src/edframe.cpp:2014 +#, c-format +msgid "%i bad token" +msgid_plural "%i bad tokens" +msgstr[0] "%i 个错误的标记" + +#: ../src/edframe.cpp:2008 +#, c-format +msgid "%i fuzzy" +msgid_plural "%i fuzzy" +msgstr[0] "%i 个模糊翻译" + +#: ../src/catalog.cpp:132 +#, c-format +msgid "%i lines of file '%s' were not loaded correctly." +msgstr "%i 行在文件 '%s' 中未正确加载。" + +#: ../src/edframe.cpp:2020 +#, c-format +msgid "%i not translated" +msgid_plural "%i not translated" +msgstr[0] "%i 个未翻译" + +#: ../src/resources/menus.xrc:213 ../src/resources/menus.xrc:214 +msgid "&About" +msgstr "关于(&A)" + +#: ../src/resources/menus.xrc:212 +msgid "&About Poedit" +msgstr "关于 Poedit(&A)" + +#: ../src/resources/menus.xrc:107 +msgid "&Automatically Translate Using TM" +msgstr "使用 TM 自动翻译(&A)" + +#: ../src/resources/menus.xrc:106 +msgid "&Automatically translate using TM" +msgstr "使用 TM 自动翻译(&A)" + +#: ../src/edframe.cpp:2786 +msgid "&Bookmarks" +msgstr "书签(&B)" + +#: ../src/resources/manager.xrc:132 ../src/resources/menus.xrc:26 +msgid "&Close" +msgstr "关闭(&C)" + +#: ../src/resources/menus.xrc:176 +msgid "&Comment Window" +msgstr "注释窗口(&C)" + +#: ../src/resources/menus.xrc:175 +msgid "&Comment window" +msgstr "注释窗口(&C)" + +#: ../src/resources/menus.xrc:130 +msgid "&Done and Next" +msgstr "完成并转到下一个(&D)" + +#: ../src/resources/menus.xrc:129 +msgid "&Done and next" +msgstr "完成并转到下一个(&D)" + +#: ../src/resources/menus.xrc:56 +msgid "&Edit" +msgstr "编辑(&E)" + +#: ../src/edframe.cpp:471 ../src/resources/manager.xrc:125 +#: ../src/resources/menus.xrc:5 +msgid "&File" +msgstr "文件(&F)" + +#: ../src/resources/menus.xrc:73 +msgid "&Find..." +msgstr "查找(&F)..." + +#: ../src/edframe.cpp:480 ../src/resources/menus.xrc:126 +msgid "&Go" +msgstr "转到(&G)" + +#: ../src/edapp.cpp:183 ../src/resources/menus.xrc:216 +msgid "&Help" +msgstr "帮助(&H)" + +#: ../src/resources/menus.xrc:14 +msgid "&New Catalog..." +msgstr "新建编目(&N)..." + +#: ../src/resources/menus.xrc:13 +msgid "&New catalog..." +msgstr "新建编目(&N)..." + +#: ../src/resources/menus.xrc:142 +msgid "&Next Message" +msgstr "下一条消息(&N)" + +#: ../src/resources/menus.xrc:141 +msgid "&Next message" +msgstr "下一条消息(&N)" + +#: ../src/resources/menus.xrc:206 +msgid "&Online Help" +msgstr "在线帮助(&O)" + +#: ../src/resources/menus.xrc:205 +msgid "&Online help" +msgstr "在线帮助(&O)" + +#: ../src/resources/menus.xrc:21 +msgid "&Open..." +msgstr "打开(&O)..." + +#: ../src/resources/menus.xrc:90 +msgid "&Preferences" +msgstr "首选项(&P)" + +#: ../src/resources/manager.xrc:128 ../src/resources/menus.xrc:44 +msgid "&Preferences..." +msgstr "首选项(&P)..." + +#: ../src/resources/menus.xrc:137 +msgid "&Previous Message" +msgstr "上一条消息(&P)" + +#: ../src/resources/menus.xrc:136 +msgid "&Previous message" +msgstr "上一条消息(&P)" + +#: ../src/resources/menus.xrc:119 +msgid "&Properties..." +msgstr "属性(&P)..." + +#: ../src/resources/menus.xrc:111 +msgid "&Purge Deleted Translations" +msgstr "清除已删除的翻译(&P)" + +#: ../src/resources/menus.xrc:110 +msgid "&Purge deleted translations" +msgstr "清除已删除的翻译(&P)" + +#: ../src/resources/menus.xrc:30 +msgid "&Save" +msgstr "保存(&S)" + +#: ../src/resources/menus.xrc:70 +msgid "&Show References" +msgstr "显示引用(&S)" + +#: ../src/resources/menus.xrc:69 +msgid "&Show references" +msgstr "显示引用(&S)" + +#: ../src/resources/menus.xrc:198 +msgid "&Untranslated Entries First" +msgstr "未翻译条目优先(&U)" + +#: ../src/resources/menus.xrc:197 +msgid "&Untranslated entries first" +msgstr "未翻译条目优先(&U)" + +#: ../src/resources/menus.xrc:99 +msgid "&Update from Sources" +msgstr "从源文更新(&U)" + +#: ../src/resources/menus.xrc:98 +msgid "&Update from sources" +msgstr "从源文更新(&U)" + +#: ../src/resources/menus.xrc:115 +msgid "&Validate Translations" +msgstr "验证翻译(&V)" + +#: ../src/resources/menus.xrc:114 +msgid "&Validate translations" +msgstr "验证翻译(&V)" + +#: ../src/resources/menus.xrc:157 +msgid "&View" +msgstr "查看(&V)" + +#: ../src/catalog.cpp:1572 +#, c-format +msgid "'%s' is not a valid POT file." +msgstr "'%s' 不是有效的 POT 文件。" + +#: ../src/summarydlg.cpp:58 +#, c-format +msgid "(%i new, %i obsolete)" +msgstr "(%i 个新建,%i 个已废弃)" + +#: ../src/resources/summary.xrc:63 +msgid "(0 new, 0 obsolete)" +msgstr "(0 个新建,0 个已废弃)" + +#: ../src/chooselang.cpp:79 +msgid "(Use default language)" +msgstr "(使用默认语言)" + +#: ../src/edframe.cpp:916 +msgid "(none of these)" +msgstr "(这些都不是)" + +#: ../src/resources/find.xrc:94 +msgid "< Previous" +msgstr "< 上一个" + +#: ../src/manager.cpp:377 +msgid "" +msgstr "<未命名>" + +#. TRANSLATORS: This is titlebar of about dialog, the string ends with space +#. and is followed by application name when used ("Poedit", +#. but don't add it to this translation yourself) (wxWidgets 2.8) +#: ../src/edframe.cpp:2435 +msgid "About " +msgstr "关于 " + +#. TRANSLATORS: This is titlebar of about dialog, "%s" is application name +#. ("Poedit" here, but please use "%s") +#: ../src/edframe.cpp:2425 +#, c-format +msgid "About %s" +msgstr "关于 %s" + +#: ../src/resources/prefs.xrc:270 +msgid "Add" +msgstr "添加" + +#: ../src/resources/manager.xrc:91 +msgid "Add directory to the list" +msgstr "添加目录到列表" + +#: ../src/transmemupd_wizard.cpp:134 ../src/resources/tm_update.xrc:85 +msgid "Add files" +msgstr "添加文件" + +#: ../src/resources/prefs.xrc:589 +msgid "Add path to the list of directories where catalogs lie." +msgstr "将路径添加到编目所在目录的列表" + +#: ../src/resources/prefs.xrc:111 +msgid "Always change focus to text input field" +msgstr "总是将焦点更改到文本输入字段" + +#: ../src/resources/prefs.xrc:514 +msgid "An item in input files list:" +msgstr "在输入文件列表中的项:" + +#: ../src/resources/prefs.xrc:495 +msgid "An item in keywords list:" +msgstr "在关键字列表中的项:" + +#: ../src/edframe.cpp:2367 ../src/edframe.cpp:2379 +msgid "Automatic Translations:" +msgstr "自动翻译:" + +#: ../src/resources/prefs.xrc:128 +msgid "Automatic spellchecking" +msgstr "自动检查拼写" + +#: ../src/export_html.cpp:173 +msgid "Automatic translation" +msgstr "自动翻译" + +#: ../src/edframe.cpp:2365 ../src/edframe.cpp:2377 +msgid "Automatic translations:" +msgstr "自动翻译:" + +#: ../src/resources/prefs.xrc:70 +msgid "Automatically check for new version of Poedit" +msgstr "自动检查 Poedit 的新版本" + +#: ../src/resources/prefs.xrc:88 +msgid "Automatically compile .mo file on save" +msgstr "保存时自动编译 .mo 文件" + +#: ../src/resources/prefs.xrc:342 +msgid "Automatically translate when updating catalog" +msgstr "当更新编目时自动翻译" + +#: ../src/edframe.cpp:2301 +#, c-format +msgid "Automatically translated %u strings" +msgstr "已自动翻译 %u 个字串" + +#: ../src/edframe.cpp:2286 +msgid "Automatically translating..." +msgstr "正在自动翻译..." + +#: ../src/manager.cpp:248 +msgid "Bad Tokens" +msgstr "错误的标记" + +#: ../src/resources/properties.xrc:136 +msgid "Base path:" +msgstr "基础路径:" + +#: ../src/resources/prefs.xrc:84 +msgid "Behavior" +msgstr "行为" + +#: ../src/catalog.cpp:680 +msgid "Broken catalog file: plural form msgstr used without msgid_plural" +msgstr "已损坏的编目文件: 在没有 msgid_plural 的情况下使用了复数形式的 msgstr" + +#: ../src/catalog.cpp:642 +msgid "" +"Broken catalog file: singular form msgstr used together with msgid_plural" +msgstr "已损坏的编目文件: 和 msgid_plural 一起使用了单数形式的 msgstr" + +#: ../src/resources/manager.xrc:90 ../src/resources/prefs.xrc:588 +#: ../src/resources/tm_update.xrc:37 +msgid "Browse" +msgstr "浏览" + +#: ../src/resources/menus.xrc:95 +msgid "C&atalog" +msgstr "编目(&A)" + +#: ../src/resources/comment.xrc:45 +msgid "C&lear" +msgstr "清除(&L)" + +#: ../src/resources/prefs.xrc:141 +msgid "CR/LF conversion" +msgstr "CR/LF 转换" + +#: ../src/resources/comment.xrc:38 ../src/resources/manager.xrc:113 +#: ../src/resources/prefs.xrc:424 ../src/resources/prefs.xrc:566 +#: ../src/resources/prefs.xrc:611 ../src/resources/progress.xrc:32 +#: ../src/resources/properties.xrc:198 +msgid "Cancel" +msgstr "取消" + +#: ../src/transmem.cpp:732 +msgid "Cannot create TM database directory!" +msgstr "不能创建 TM 数据库目录!" + +#: ../src/utility.cpp:57 ../src/utility.cpp:67 +msgid "Cannot create temporary directory." +msgstr "不能创建临时目录。" + +#: ../src/gexecute.cpp:100 +#, c-format +msgid "Cannot execute program: %s" +msgstr "不能执行程序: %s" + +#: ../src/transmemupd.cpp:199 +msgid "Cannot extract catalogs from RPM file." +msgstr "不能从 RPM 文件提取编目。" + +#: ../src/resources/find.xrc:36 +msgid "Case sensitive" +msgstr "区分大小写" + +#: ../src/manager.cpp:244 +msgid "Catalog" +msgstr "编目" + +#: ../src/edframe.cpp:987 +msgid "Catalog modified. Do you want to save changes?" +msgstr "编目已修改。您想要保存更改吗?" + +#: ../src/resources/properties.xrc:4 +msgid "Catalog properties" +msgstr "编目属性" + +#: ../src/resources/menus.xrc:9 +msgid "Catalogs &Manager" +msgstr "编目管理器(&M)" + +#: ../src/resources/menus.xrc:8 +msgid "Catalogs &manager" +msgstr "编目管理器(&M)" + +#: ../src/resources/prefs.xrc:63 +msgid "Change UI language" +msgstr "更改 UI 语言" + +#: ../src/export_html.cpp:119 ../src/resources/properties.xrc:74 +msgid "Charset:" +msgstr "字符集:" + +#: ../src/edframe.cpp:483 +msgid "Check for Updates..." +msgstr "检查更新..." + +#: ../src/resources/toolbar.xrc:39 +msgid "Check for errors in the translation" +msgstr "检查翻译中的错误" + +#: ../src/resources/prefs.xrc:204 ../src/resources/prefs.xrc:230 +msgid "Choose" +msgstr "选择" + +#: ../src/edframe.cpp:2339 ../src/resources/menus.xrc:64 +msgid "Clear Translation" +msgstr "清除翻译" + +#: ../src/resources/comment.xrc:46 +msgid "Clear the comment" +msgstr "清除注释" + +#: ../src/edframe.cpp:2337 ../src/resources/menus.xrc:63 +msgid "Clear translation" +msgstr "清除翻译" + +#: ../src/resources/find.xrc:87 +msgid "Close" +msgstr "关闭" + +#: ../src/resources/toolbar.xrc:57 +msgid "Comment" +msgstr "注释" + +#: ../src/resources/prefs.xrc:120 +msgid "Comment window is editable" +msgstr "注释窗口可编辑" + +#: ../src/edframe.cpp:547 ../src/resources/comment.xrc:10 +msgid "Comment:" +msgstr "注释:" + +#: ../src/resources/prefs.xrc:292 +msgid "Configuration" +msgstr "配置" + +#: ../src/manager.cpp:407 ../src/manager.cpp:427 +msgid "Confirmation" +msgstr "确认" + +#: ../src/edframe.cpp:1812 +msgid "Context:" +msgstr "上下文:" + +#: ../src/edframe.cpp:2332 ../src/resources/menus.xrc:59 +msgid "Copy from Source Text" +msgstr "从源文文本中复制" + +#: ../src/edframe.cpp:2330 ../src/resources/menus.xrc:58 +msgid "Copy from source text" +msgstr "从源文文本中复制" + +#: ../src/catalog.cpp:1038 +#, c-format +msgid "Couldn't load file %s, it is probably corrupted." +msgstr "不能加载文件 %s,可能已损坏。" + +#: ../src/catalog.cpp:1307 +#, c-format +msgid "Couldn't save file %s." +msgstr "不能保存文件 %s。" + +#: ../src/resources/manager.xrc:44 +msgid "Create new translations project" +msgstr "创建新的翻译项目" + +#: ../src/resources/prefs.xrc:252 +msgid "Database" +msgstr "数据库" + +#: ../src/resources/manager.xrc:53 ../src/resources/prefs.xrc:390 +msgid "Delete" +msgstr "删除" + +#: ../src/editlbox/editlbox.cpp:171 +msgid "Delete item" +msgstr "删除项" + +#: ../src/resources/manager.xrc:54 +msgid "Delete the project" +msgstr "删除项目" + +#: ../src/manager.cpp:300 +msgid "Directories:" +msgstr "目录:" + +#: ../src/resources/menus.xrc:165 +msgid "Display &Line Numbers" +msgstr "显示行号(&L)" + +#: ../src/resources/menus.xrc:170 +msgid "Display &Notes for Translators" +msgstr "显示给译员的附注(&N)" + +#: ../src/resources/menus.xrc:160 +msgid "Display &Quotes" +msgstr "显示引号(&Q)" + +#: ../src/resources/menus.xrc:164 +msgid "Display &line numbers" +msgstr "显示行号(&L)" + +#: ../src/resources/menus.xrc:169 +msgid "Display ¬es for translators" +msgstr "显示给译员的附注(&N)" + +#: ../src/resources/menus.xrc:159 +msgid "Display "es" +msgstr "显示引号(&Q)" + +#: ../src/manager.cpp:426 +msgid "" +"Do you really want to do mass update of\n" +"all catalogs in this project?" +msgstr "" +"您确实想要执行这个项目中所有编目的\n" +"大量更新吗?" + +#: ../src/manager.cpp:406 +msgid "Do you want to delete the project?" +msgstr "您想要删除项目吗?" + +#: ../src/edframe.cpp:2227 +msgid "Do you want to remove all translations that are no longer used?" +msgstr "您想要移除不再使用的所有翻译吗?" + +#: ../src/edframe.cpp:999 +msgid "Don't Save" +msgstr "不保存" + +#: ../src/resources/prefs.xrc:170 +msgid "Don't change format of existing catalogs" +msgstr "不更改现有编目的格式" + +#: ../src/edframe.cpp:997 +msgid "Don't save" +msgstr "不保存" + +#: ../src/attentionbar.cpp:195 +msgid "Don't show again" +msgstr "不再显示" + +#: ../src/resources/manager.xrc:136 ../src/resources/menus.xrc:51 +msgid "E&xit" +msgstr "退出(&X)" + +#: ../src/resources/menus.xrc:39 +msgid "E&xport..." +msgstr "导出(&X)..." + +#: ../src/resources/manager.xrc:48 ../src/resources/prefs.xrc:383 +msgid "Edit" +msgstr "编辑" + +#: ../src/resources/menus.xrc:85 +msgid "Edit &Comment" +msgstr "编辑注释(&C)" + +#: ../src/resources/menus.xrc:84 +msgid "Edit &comment" +msgstr "编辑注释(&C)" + +#: ../src/edframe.cpp:2346 +msgid "Edit Comment" +msgstr "编辑注释" + +#: ../src/edframe.cpp:2344 ../src/resources/comment.xrc:4 +#: ../src/resources/toolbar.xrc:58 +msgid "Edit comment" +msgstr "编辑注释" + +#: ../src/editlbox/editlbox.cpp:169 +msgid "Edit item" +msgstr "编辑项" + +#: ../src/resources/manager.xrc:64 +msgid "Edit project" +msgstr "编辑项目" + +#: ../src/resources/manager.xrc:49 +msgid "Edit the project" +msgstr "编辑项目" + +#: ../src/resources/prefs.xrc:78 +msgid "Editor" +msgstr "编辑器" + +#: ../src/resources/prefs.xrc:129 +msgid "Enables on-the-fly spellchecking" +msgstr "启用运行中拼写检查" + +#: ../src/edframe.cpp:1309 +msgid "Entries in the catalog are probably incorrect." +msgstr "编目中的条目可能不正确。" + +#: ../src/edframe.cpp:1911 +msgid "" +"Entries in this catalog have different plural forms count from what " +"catalog's Plural-Forms header says" +msgstr "这个编目中的条目的复数形式数量与编目的 Plural-Forms 头所说明的不同" + +#: ../src/edframe.cpp:1376 +msgid "" +"Entries with errors were marked in red in the list. Details of the error " +"will be shown when you select such an entry." +msgstr "" +"有错误的条目在列表中被标记为红色。当您选择这样的条目时将显示错误的详细信息。" + +#: ../src/edframe.cpp:1960 +#, c-format +msgid "Error loading message catalog file '%s'." +msgstr "加载消息编目文件 '%s' 时存在错误。" + +#: ../src/fileviewer.cpp:226 +#, c-format +msgid "Error opening file %s!" +msgstr "打开文件 %s 时存在错误!" + +#: ../src/catalog.cpp:1354 +msgid "Error saving catalog" +msgstr "保存编目时存在错误" + +#: ../src/errorbar.cpp:60 +msgid "Error:" +msgstr "错误:" + +#: ../src/edframe.cpp:1138 +msgid "Export as..." +msgstr "导出为..." + +#: ../src/resources/properties.xrc:127 +msgid "Extract text from source files in the following directories:" +msgstr "从下列目录中的源文件提取文本:" + +#: ../src/digger.cpp:60 +#, c-format +msgid "Failed command: %s" +msgstr "失败的命令: %s" + +#: ../src/digger.cpp:114 +msgid "Failed to load extracted catalog." +msgstr "未能加载已提取的编目。" + +#: ../src/digger.cpp:61 +msgid "Failed to merge gettext catalogs." +msgstr "未能合并 gettext 编目。" + +#: ../src/edframe.cpp:393 ../src/edframe.cpp:1061 +#, c-format +msgid "File '%s' doesn't exist." +msgstr "文件 '%s' 不存在。" + +#: ../src/edframe.cpp:386 +#, c-format +msgid "File '%s' is not a message catalog." +msgstr "文件 '%s' 不是消息编目。" + +#: ../src/catalog.cpp:1268 +#, c-format +msgid "" +"File '%s' is read-only and cannot be saved.\n" +"Please save it under different name." +msgstr "" +"文件 '%s' 为只读,不能保存。\n" +"请用不同的名称保存。" + +#: ../src/transmemupd_wizard.cpp:59 +msgid "Files List" +msgstr "文件列表" + +#: ../src/resources/find.xrc:78 +msgid "Find in automatic comments" +msgstr "在自动注释中查找" + +#: ../src/resources/find.xrc:71 +msgid "Find in comments" +msgstr "在注释中查找" + +#: ../src/resources/find.xrc:57 +msgid "Find in original strings" +msgstr "在原始字串中查找" + +#: ../src/resources/find.xrc:64 +msgid "Find in translations" +msgstr "在翻译中查找" + +#: ../src/resources/find.xrc:4 +msgid "Find..." +msgstr "查找..." + +#: ../src/edframe.cpp:1941 +msgid "Fix the header" +msgstr "修补头" + +#: ../src/resources/prefs.xrc:181 +msgid "Fonts" +msgstr "字体" + +#: ../src/edframe.cpp:2719 +#, c-format +msgid "Form %i" +msgstr "形式 %i" + +#: ../src/edframe.cpp:2721 +#, c-format +msgid "Form %i (e.g. \"%u\")" +msgstr "形式 %i (例如 \"%u\")" + +#: ../src/manager.cpp:247 ../src/resources/toolbar.xrc:51 +msgid "Fuzzy" +msgstr "模糊" + +#: ../src/export_html.cpp:179 +msgid "Fuzzy translation" +msgstr "模糊翻译" + +#: ../src/edframe.cpp:1043 ../src/edframe.cpp:1102 +msgid "GNU gettext catalogs (*.po)|*.po|All files (*.*)|*.*" +msgstr "GNU gettext 编目(*.po)|*.po|所有文件(*.*)|*.*" + +#: ../src/edframe.cpp:1176 ../src/edframe.cpp:1327 +msgid "GNU gettext templates (*.pot)|*.pot|All files (*.*)|*.*" +msgstr "GNU gettext 模板(*.pot)|*.pot|所有文件(*.*)|*.*" + +#: ../src/resources/prefs.xrc:621 +msgid "Generate TM database" +msgstr "生成 TM 数据库" + +#: ../src/resources/prefs.xrc:278 +msgid "Generate database" +msgstr "生成数据库" + +#: ../src/edframe.cpp:2798 +#, c-format +msgid "Go to Bookmark %i\tCtrl+%i" +msgstr "转到书签 %i\tCtrl+%i" + +#: ../src/edframe.cpp:2792 +#, c-format +msgid "Go to Bookmark %i\tCtrl+Alt+%i" +msgstr "转到书签 %i\tCtrl+Alt+%i" + +#: ../src/edframe.cpp:2795 +#, c-format +msgid "Go to bookmark %i\tCtrl+%i" +msgstr "转到书签 %i\tCtrl+%i" + +#: ../src/edframe.cpp:1140 +msgid "HTML file (*.html)|*.html" +msgstr "HTML 文件(*.html)|*.html" + +#: ../src/attentionbar.cpp:78 +msgid "Hide this notification message" +msgstr "隐藏这个通知消息" + +#: ../src/resources/prefs.xrc:18 +msgid "Identity" +msgstr "身份" + +#: ../src/resources/prefs.xrc:121 +msgid "If checked, the comment window will be editable." +msgstr "如果选中,则注释窗口将可编辑。" + +#: ../src/edframe.cpp:2229 +msgid "" +"If you continue with purging, all translations marked as deleted will be " +"permanently removed. You will have to translate them again if they are added " +"back in the future." +msgstr "" +"如果您继续清除,则所有被标记为已删除的翻译都将被永久移除。如果将来它们被添加" +"回来,则您必须再翻译一遍。" + +#: ../src/resources/prefs.xrc:472 +msgid "Invocation:" +msgstr "调用:" + +#: ../src/edframe.cpp:2234 ../src/transmem.cpp:1202 +msgid "Keep" +msgstr "保持" + +#: ../src/propertiesdlg.cpp:58 +msgid "Keywords" +msgstr "关键字" + +#: ../src/chooselang.cpp:159 +msgid "Language selection" +msgstr "语言选择" + +#: ../src/export_html.cpp:108 ../src/resources/prefs.xrc:440 +#: ../src/resources/prefs.xrc:444 ../src/resources/properties.xrc:62 +msgid "Language:" +msgstr "语言:" + +#: ../src/manager.cpp:249 +msgid "Last modified" +msgstr "最后修改" + +#: ../src/resources/properties.xrc:111 +msgid "Learn about plural forms" +msgstr "学习复数形式" + +#: ../src/edframe.cpp:889 +msgid "Learn more" +msgstr "学习更多" + +#: ../src/edlistctrl.cpp:328 +msgid "Line" +msgstr "行" + +#: ../src/catalog.cpp:145 +#, c-format +msgid "Line %u of file '%s' is corrupted (not valid %s data)." +msgstr "行 %u 在文件 '%s' 中已损坏(不是有效的 %s 数据)。" + +#: ../src/resources/prefs.xrc:149 +msgid "Line endings format:" +msgstr "行结束格式:" + +#: ../src/resources/prefs.xrc:456 +msgid "List of extensions separated by semicolons (e.g. *.cpp;*.h):" +msgstr "用分号分隔的扩展名列表(例如 *.cpp;*.h):" + +#: ../src/catalog.cpp:220 +#, c-format +msgid "Malformed header: '%s'" +msgstr "错误的头: '%s'" + +#: ../src/resources/prefs.xrc:300 +msgid "Max. # of missing words:" +msgstr "缺少单词的最大数量:" + +#: ../src/resources/prefs.xrc:322 +msgid "Max. difference in sentence length:" +msgstr "句子长度的最大差异:" + +#: ../src/catalog.cpp:1543 +msgid "Merging differences..." +msgstr "合并差异..." + +#: ../src/editlbox/editlbox.cpp:173 +msgid "Move down" +msgstr "下移" + +#: ../src/editlbox/editlbox.cpp:172 +msgid "Move up" +msgstr "上移" + +#: ../src/prefsdlg.cpp:55 +msgid "My Languages" +msgstr "我的语言" + +#: ../src/resources/menus.xrc:152 +msgid "Ne&xt Unfinished" +msgstr "下一个未完成(&X)" + +#: ../src/resources/menus.xrc:151 +msgid "Ne&xt unfinished" +msgstr "下一个未完成(&X)" + +#: ../src/resources/prefs.xrc:113 +msgid "" +"Never let the list of strings take focus. If enabled, you must use Ctrl-" +"arrows for keyboard navigation but you can also type text immediately, " +"without having to press Tab to change focus." +msgstr "" +"从不让字串列表取得焦点。如果启用,您必须使用 “Ctrl-方向键” 进行键盘导航,但您" +"也可以立即输入文本,不用按 Tab 改变焦点。" + +#: ../src/resources/manager.xrc:43 ../src/resources/prefs.xrc:378 +msgid "New" +msgstr "新建" + +#: ../src/resources/menus.xrc:18 +msgid "New Catalog from POT File..." +msgstr "从 POT 文件新建编目..." + +#: ../src/resources/menus.xrc:17 +msgid "New catalog from POT file..." +msgstr "从 POT 文件新建编目..." + +#: ../src/editlbox/editlbox.cpp:170 +msgid "New item" +msgstr "新建项" + +#: ../src/resources/summary.xrc:30 +msgid "New strings" +msgstr "新建字串" + +#: ../src/resources/find.xrc:102 +msgid "Next >" +msgstr "下一个 >" + +#: ../src/digger.cpp:201 +msgid "No files found in: " +msgstr "在此位置中未找到文件: " + +#: ../src/edframe.cpp:1391 +msgid "No problems with the translation found." +msgstr "翻译中未发现问题。" + +#: ../src/edframe.cpp:1433 +msgid "No references to this string found." +msgstr "未找到对这个字串的引用。" + +#: ../src/export_html.cpp:151 +msgid "Notes" +msgstr "附注" + +#: ../src/edframe.cpp:550 +msgid "Notes for translators:" +msgstr "给译员的附注:" + +#: ../src/resources/comment.xrc:30 ../src/resources/manager.xrc:105 +#: ../src/resources/prefs.xrc:416 ../src/resources/prefs.xrc:558 +#: ../src/resources/properties.xrc:190 ../src/resources/summary.xrc:71 +msgid "OK" +msgstr "确定" + +#: ../src/resources/summary.xrc:51 +msgid "Obsolete strings" +msgstr "已废弃的字串" + +#: ../src/resources/toolbar.xrc:15 ../src/resources/toolbar.xrc:20 +msgid "Open" +msgstr "打开" + +#: ../src/edframe.cpp:1041 ../src/resources/toolbar.xrc:16 +#: ../src/resources/toolbar.xrc:21 +msgid "Open catalog" +msgstr "打开编目" + +#: ../src/edframe.cpp:1174 ../src/edframe.cpp:1325 +msgid "Open catalog template" +msgstr "打开编目模板" + +#: ../src/resources/prefs.xrc:104 +msgid "Open catalogs manager on Poedit startup" +msgstr "在 Poedit 启动时打开编目管理器" + +#: ../src/resources/menus.xrc:147 +msgid "P&revious Unfinished" +msgstr "上一个未完成(&R)" + +#: ../src/resources/menus.xrc:146 +msgid "P&revious unfinished" +msgstr "上一个未完成(&R)" + +#: ../src/resources/prefs.xrc:476 +msgid "Parser command:" +msgstr "分析器命令:" + +#: ../src/resources/prefs.xrc:435 +msgid "Parser setup" +msgstr "分析器设置" + +#: ../src/resources/prefs.xrc:353 +msgid "Parsers" +msgstr "分析器" + +#: ../src/digger.cpp:92 +#, c-format +msgid "Parsing %s files..." +msgstr "正在分析 %s 文件..." + +#: ../src/propertiesdlg.cpp:60 +msgid "Paths" +msgstr "路径" + +#: ../src/resources/prefs.xrc:12 +msgid "Personalize" +msgstr "个人化" + +#: ../src/resources/prefs.xrc:271 +msgid "Pick language from the list of known languages" +msgstr "从已知语言的列表中挑选语言" + +#: ../src/resources/tm_update.xrc:21 +msgid "Please add directories where locale files are stored on your system:" +msgstr "请添加地区(locale)文件在您的系统上存储的目录:" + +#: ../src/edframe.cpp:1441 +msgid "Please choose the reference you want to show:" +msgstr "请选择您想要显示的引用:" + +#: ../src/prefsdlg.cpp:334 +msgid "Please select language ISO code:" +msgstr "请选择语言 ISO 代码:" + +#: ../src/edframe.cpp:925 +msgid "Please select language code:" +msgstr "请选择语言代码:" + +#: ../src/transmem.cpp:1196 +#, c-format +msgid "" +"Please verify that all files were moved to the new location or do it " +"manually if they weren't.\n" +"\n" +"Old location: %s\n" +"New location: %s" +msgstr "" +"请核实所有的文件都被移动到新位置,如果没有,则手动执行。\n" +"\n" +"旧位置: %s\n" +"新位置: %s" + +#: ../src/resources/properties.xrc:98 +msgid "Plural Forms:" +msgstr "复数形式:" + +#: ../src/edframe.cpp:564 +msgid "Plural:" +msgstr "复数:" + +#: ../src/edframe.cpp:410 +msgid "Poedit" +msgstr "Poedit" + +#: ../src/manager.cpp:69 +msgid "Poedit - Catalogs manager" +msgstr "Poedit - 编目管理器" + +#: ../src/digger.cpp:202 +msgid "Poedit did not find any files in scanned directories." +msgstr "Poedit 在已扫描的目录中未找到任何文件。" + +#: ../src/edframe.cpp:2443 +msgid "Poedit is an easy to use translations editor." +msgstr "Poedit 是一个易于使用的翻译编辑器。" + +#: ../src/transmem.cpp:1191 +msgid "Poedit translation memory error" +msgstr "Poedit 翻译记忆错误" + +#: ../src/resources/prefs.xrc:4 +msgid "Preferences" +msgstr "首选项" + +#: ../src/resources/prefs.xrc:603 +msgid "Proceed" +msgstr "继续进行" + +#: ../src/export_html.cpp:101 +msgid "Project info" +msgstr "项目信息" + +#: ../src/export_html.cpp:105 ../src/resources/properties.xrc:19 +msgid "Project name and version:" +msgstr "项目名称和版本:" + +#: ../src/resources/manager.xrc:69 +msgid "Project name:" +msgstr "项目名称:" + +#: ../src/edframe.cpp:2234 ../src/transmem.cpp:1202 +msgid "Purge" +msgstr "清除" + +#: ../src/edframe.cpp:2225 +msgid "Purge deleted translations" +msgstr "清除已删除的翻译" + +#: ../src/resources/manager.xrc:137 ../src/resources/menus.xrc:52 +msgid "Quit" +msgstr "退出" + +#: ../src/edframe.cpp:1441 +msgid "References" +msgstr "引用" + +#: ../src/edframe.cpp:2401 ../src/edframe.cpp:2405 +msgid "References:" +msgstr "引用:" + +#: ../src/resources/prefs.xrc:279 +msgid "Regenerate translation memory from catalogs in paths listed above." +msgstr "从上面列出的路径中的编目重新生成翻译记忆。" + +#: ../src/edframe.cpp:1922 +msgid "Required header Plural-Forms is missing." +msgstr "缺少必需的头 Plural-Forms。" + +#: ../src/resources/tm_update.xrc:42 +msgid "Reset to defaults" +msgstr "重置为默认值" + +#: ../src/edframe.cpp:995 ../src/resources/toolbar.xrc:25 +#: ../src/resources/toolbar.xrc:30 +msgid "Save" +msgstr "保存" + +#: ../src/resources/menus.xrc:35 +msgid "Save &As..." +msgstr "另存为(&A)..." + +#: ../src/resources/menus.xrc:34 +msgid "Save &as..." +msgstr "另存为(&A)..." + +#: ../src/edframe.cpp:1101 +msgid "Save as..." +msgstr "另存为..." + +#: ../src/resources/toolbar.xrc:26 ../src/resources/toolbar.xrc:31 +msgid "Save catalog" +msgstr "保存编目" + +#: ../src/edframe.cpp:988 +msgid "Save changes" +msgstr "保存更改" + +#: ../src/transmemupd.cpp:149 +msgid "Scanning file: " +msgstr "正在扫描文件: " + +#: ../src/digger.cpp:77 +msgid "Scanning files..." +msgstr "正在扫描文件..." + +#: ../src/transmemupd_wizard.cpp:55 +msgid "Search Paths" +msgstr "搜索路径" + +#: ../src/edframe.cpp:924 +msgid "Select catalog's language" +msgstr "选择编目的语言" + +#: ../src/manager.cpp:280 ../src/transmemupd_wizard.cpp:108 +msgid "Select directory" +msgstr "选择目录" + +#: ../src/prefsdlg.cpp:333 +msgid "Select language" +msgstr "选择语言" + +#: ../src/chooselang.cpp:158 +msgid "Select your prefered language" +msgstr "选择您的首选语言" + +#: ../src/edframe.cpp:2797 +#, c-format +msgid "Set Bookmark %i\tAlt+%i" +msgstr "设置书签 %i\tAlt+%i" + +#: ../src/edframe.cpp:2791 +#, c-format +msgid "Set Bookmark %i\tCtrl+%i" +msgstr "设置书签 %i\tCtrl+%i" + +#: ../src/edframe.cpp:2794 +#, c-format +msgid "Set bookmark %i\tAlt+%i" +msgstr "设置书签 %i\tAlt+%i" + +#: ../src/edframe.cpp:1893 +msgid "Set email" +msgstr "设置电子邮件" + +#: ../src/resources/prefs.xrc:97 +msgid "Show summary after catalog update" +msgstr "在编目更新之后显示摘要" + +#: ../src/edframe.cpp:562 +msgid "Singular:" +msgstr "单数:" + +#: ../src/resources/menus.xrc:182 +msgid "Sort by &File Order" +msgstr "按文件顺序排序(&F)" + +#: ../src/resources/menus.xrc:187 +msgid "Sort by &Source" +msgstr "按源文排序(&S)" + +#: ../src/resources/menus.xrc:192 +msgid "Sort by &Translation" +msgstr "按翻译排序(&T)" + +#: ../src/resources/menus.xrc:181 +msgid "Sort by &file order" +msgstr "按文件顺序排序(&F)" + +#: ../src/resources/menus.xrc:186 +msgid "Sort by &source" +msgstr "按源文排序(&S)" + +#: ../src/resources/menus.xrc:191 +msgid "Sort by &translation" +msgstr "按翻译排序(&T)" + +#: ../src/export_html.cpp:145 +msgid "Source" +msgstr "源文" + +#: ../src/resources/prefs.xrc:533 ../src/resources/properties.xrc:86 +msgid "Source code charset:" +msgstr "源代码字符集:" + +#: ../src/resources/prefs.xrc:359 +msgid "Source code parsers:" +msgstr "源代码分析器:" + +#: ../src/fileviewer.cpp:46 +msgid "Source file" +msgstr "源文件" + +#: ../src/fileviewer.cpp:61 +msgid "Source file occurrence:" +msgstr "源文件出现:" + +#: ../src/edlistctrl.cpp:325 +msgid "Source text" +msgstr "源文文本" + +#: ../src/edframe.cpp:536 +msgid "Source text:" +msgstr "源文文本:" + +#: ../src/resources/properties.xrc:179 +msgid "Sources keywords" +msgstr "源关键字" + +#: ../src/resources/properties.xrc:158 +msgid "Sources paths" +msgstr "源路径" + +#. TRANSLATORS: %s is language name in its basic form (as you +#. would see e.g. in a list of supported languages). +#: ../src/edframe.cpp:885 +#, c-format +msgid "Spellchecker dictionary for %s isn't available, you need to install it." +msgstr "供 %s 使用的拼写检查器字典不可用,您需要安装。" + +#: ../src/resources/find.xrc:43 +msgid "Start from the first item" +msgstr "从第一项开始" + +#: ../src/resources/find.xrc:21 +msgid "String to find:" +msgstr "要查找的字串:" + +#: ../src/edframe.cpp:1927 +#, c-format +msgid "Syntax error in Plural-Forms header (\"%s\")." +msgstr "在 Plural-Forms 头中存在语法错误(\"%s\")。" + +#: ../src/export_html.cpp:115 ../src/resources/properties.xrc:48 +msgid "Team's email address:" +msgstr "团队的电子邮件地址:" + +#: ../src/export_html.cpp:111 ../src/resources/properties.xrc:34 +msgid "Team:" +msgstr "团队:" + +#: ../src/catalog.cpp:1353 +#, c-format +msgid "" +"The catalog couldn't be saved in '%s' charset as\n" +"specified in catalog settings. It was saved in UTF-8 instead\n" +"and the setting was modified accordingly." +msgstr "" +"不能按在编目设置中所指定的将编目保存为 '%s' 字符集。\n" +"改为将其保存为 UTF-8,\n" +"设置也相应地被修改。" + +#: ../src/edframe.cpp:1380 +msgid "" +"The file was saved safely, but it cannot be compiled into the MO format and " +"used." +msgstr "文件已安全地保存,但它不能被编译成 MO 格式并使用。" + +#: ../src/edframe.cpp:1396 +msgid "The translation is ready for use." +msgstr "翻译为使用准备就绪。" + +#: ../src/catalog.cpp:1311 +msgid "" +"There was a problem formatting the file nicely (but it was saved all right)." +msgstr "在精确格式化文件时有一个问题(但文件保存正确)。" + +#: ../src/transmem.cpp:1193 +msgid "There was a problem moving your translation memory." +msgstr "在移动您的翻译记忆时有一个问题。" + +#: ../src/catalog.cpp:1030 +msgid "" +"There were errors when loading the catalog. Some data may be missing or " +"corrupted as the result." +msgstr "当加载编目时有错误。因此有些数据可能缺少或损坏。" + +#: ../src/resources/summary.xrc:38 +msgid "" +"These strings are no longer in the sources.\n" +"Poedit will remove them from the catalog now." +msgstr "" +"源文件中不再有这些字串。\n" +"Poedit 现在将从编目中移除这些字串。" + +#: ../src/resources/summary.xrc:17 +msgid "" +"These strings were found in the sources but were not in the catalog.\n" +"Poedit will add them to the catalog now." +msgstr "" +"在源文中找到这些字串,但编目中没有。\n" +"Poedit 现在将把这些字串添加到编目。" + +#: ../src/edframe.cpp:1907 +msgid "" +"This catalog has entries with plural forms, but doesn't have Plural-Forms " +"header configured." +msgstr "这个编目有带有复数形式的条目,但没有已配置的 Plural-Forms 头。" + +#: ../src/resources/prefs.xrc:488 +msgid "" +"This is the command used to launch the parser.\n" +"%o expands to the name of output file, %K to list\n" +"of keywords, %F to list of input files,\n" +"%C to charset flag (see below)." +msgstr "" +"这是用来启动分析器的命令。\n" +"%o 展开成输出文件的名称,%K 展开成关键字的列表,\n" +"%F 展开成输入文件的列表,%C 展开成字符集标记(见下面的设置)。" + +#: ../src/resources/prefs.xrc:545 +#, c-format +msgid "" +"This will be attached to the command line\n" +"only if source codecharset was given. %c expands to charset value." +msgstr "" +"仅在指定了源代码字符集时,这才被附加到命令行。\n" +"%c 展开成字符集值。" + +#: ../src/resources/prefs.xrc:526 +#, c-format +msgid "" +"This will be attached to the command line once\n" +"for each input file. %f expands to the filename." +msgstr "" +"对于每个输入文件,这将被附加到命令行一次。\n" +"%f 展开成文件名。" + +#: ../src/resources/prefs.xrc:507 +msgid "" +"This will be attached to the command line once\n" +"for each keyword. %k expands to the keyword." +msgstr "" +"对于每个关键字,这将被附加到命令行一次。\n" +"%k 展开成关键字。" + +#: ../src/resources/toolbar.xrc:52 +msgid "Toggled if selected string has fuzzy translation" +msgstr "切换是否选择的字串有模糊翻译" + +#: ../src/manager.cpp:245 +msgid "Total" +msgstr "总计" + +#: ../src/edlistctrl.cpp:326 ../src/export_html.cpp:148 +msgid "Translation" +msgstr "翻译" + +#: ../src/resources/menus.xrc:79 +msgid "Translation Is &Fuzzy" +msgstr "翻译是模糊翻译(&F)" + +#: ../src/resources/prefs.xrc:245 +msgid "Translation Memory" +msgstr "翻译记忆" + +#: ../src/transmemupd_wizard.cpp:140 +msgid "Translation files (*.po;*.mo)|*.po;*.mo" +msgstr "翻译文件(*.po;*.mo)|*.po;*.mo" + +#: ../src/transmemupd_wizard.cpp:138 +msgid "Translation files (*.po;*.mo;*.rpm)|*.po;*.mo;*.rpm" +msgstr "翻译文件(*.po;*.mo;*.rpm)|*.po;*.mo;*.rpm" + +#: ../src/resources/menus.xrc:78 +msgid "Translation is &fuzzy" +msgstr "翻译是模糊翻译(&F)" + +#: ../src/transmem.cpp:661 +#, c-format +msgid "Translation memory database error: %s" +msgstr "翻译记忆数据库错误: %s" + +#: ../src/resources/tm_update.xrc:68 +msgid "" +"Translation memory will be built from the files listed below.\n" +"You can add more files to the list now." +msgstr "" +"翻译记忆将从下面列出的文件构建。\n" +"您现在可以将更多文件添加到列表。" + +#: ../src/resources/properties.xrc:119 +msgid "Translation properties" +msgstr "翻译属性" + +#: ../src/edframe.cpp:544 +msgid "Translation:" +msgstr "翻译:" + +#: ../src/propertiesdlg.cpp:73 +msgid "UTF-8 (recommended)" +msgstr "UTF-8 (推荐)" + +#: ../src/resources/summary.xrc:79 +msgid "Undo" +msgstr "撤销" + +#: ../src/resources/prefs.xrc:157 +msgid "Unix (recommended)" +msgstr "Unix (推荐)" + +#: ../src/chooselang.cpp:60 +#, c-format +msgid "Unknown locale code '%s' in registry." +msgstr "注册表中存在未知的地区(locale)代码 '%s'。" + +#: ../src/manager.cpp:246 +msgid "Untrans" +msgstr "未翻译" + +#: ../src/resources/toolbar.xrc:44 +msgid "Update" +msgstr "更新" + +#: ../src/resources/manager.xrc:59 +msgid "Update all" +msgstr "更新全部" + +#: ../src/resources/manager.xrc:60 +msgid "Update all catalogs in the project" +msgstr "更新项目中的所有编目" + +#: ../src/resources/toolbar.xrc:45 +msgid "Update catalog - synchronize it with sources" +msgstr "更新编目 - 将其与源文同步" + +#: ../src/resources/menus.xrc:103 +msgid "Update from &POT File..." +msgstr "从 POT 文件更新(&P)..." + +#: ../src/resources/menus.xrc:102 +msgid "Update from &POT file..." +msgstr "从 POT 文件更新(&P)..." + +#: ../src/resources/summary.xrc:4 +msgid "Update summary" +msgstr "更新摘要" + +#: ../src/resources/tm_update.xrc:4 +msgid "Update translation memory" +msgstr "更新翻译记忆" + +#: ../src/edframe.cpp:1297 ../src/manager.cpp:435 +msgid "Updating catalog" +msgstr "更新编目" + +#: ../src/edframe.cpp:1311 +msgid "Updating the catalog failed. Click on 'Details >>' for details." +msgstr "更新编目失败。单击 '详细资料 >>' 了解详细信息。" + +#: ../src/transmemupd_wizard.cpp:194 +msgid "Updating translation memory" +msgstr "更新翻译记忆" + +#: ../src/resources/prefs.xrc:213 +msgid "Use custom font for text fields" +msgstr "为文本字段使用自定义字体" + +#: ../src/resources/prefs.xrc:187 +msgid "Use custom font for translations list" +msgstr "为翻译列表使用自定义字体" + +#: ../src/resources/properties.xrc:166 +msgid "" +"Use these keywords (function names) to recognize translatable strings\n" +"in source files:" +msgstr "使用这些关键字(函数名)来识别源文件中的可翻译字串:" + +#: ../src/resources/toolbar.xrc:38 +msgid "Validate" +msgstr "验证" + +#: ../src/edframe.cpp:1372 ../src/edframe.cpp:1392 +msgid "Validation results" +msgstr "验证结果" + +#. TRANSLATORS: This is version information in about dialog, "%s" will be +#. version number when used +#: ../src/edframe.cpp:2428 +#, c-format +msgid "Version %s" +msgstr "版本 %s" + +#: ../src/resources/prefs.xrc:255 +msgid "What languages do you want to use the TM with?" +msgstr "您希望 TM 使用什么语言?" + +#: ../src/resources/find.xrc:50 +msgid "Whole words only" +msgstr "仅整个单词" + +#: ../src/resources/prefs.xrc:158 +msgid "Windows" +msgstr "Windows" + +#: ../src/edframe.cpp:378 +msgid "You can't drop more than one file on Poedit window." +msgstr "您不能在 Poedit 窗口上放下一个以上的文件。" + +#: ../src/chooselang.cpp:173 +msgid "You must restart Poedit for this change to take effect." +msgstr "您必须重新启动 Poedit 才能使这个更改生效。" + +#: ../src/edframe.cpp:1891 +msgid "" +"You should set your email address in Preferences so that it can be used for " +"Last-Translator header in GNU gettext files." +msgstr "" +"您应该在 “首选项” 中设置您的电子邮件地址,以便它可以供在 GNU gettext 文件中" +"的 Last-Translator 头使用。" + +#: ../src/edframe.cpp:992 +msgid "Your changes will be lost if you don't save them." +msgstr "如果您不保存,则您的更改将丢失。" + +#: ../src/resources/prefs.xrc:44 +msgid "Your email address:" +msgstr "您的电子邮件地址:" + +#: ../src/resources/prefs.xrc:22 +msgid "" +"Your name and email set below are only used\n" +"to set the Last-Translator header of GNU gettext files." +msgstr "" +"下面设置的您的名字和电子邮件仅用于设置\n" +" GNU gettext 文件的 Last-Translator 头。" + +#: ../src/resources/prefs.xrc:30 +msgid "Your name:" +msgstr "您的名字:" + +#: ../src/edapp.cpp:372 +msgid "don't delete temporary files (for debugging)" +msgstr "不删除临时文件(供调试使用)" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.mo b/vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.mo new file mode 100644 index 0000000000000000000000000000000000000000..76fa3995b53fd51805a6d788223fd78aa2da40d9 GIT binary patch literal 512 zcmZvZJx{|h5QYsDBqFgeGk8Z=Qedb;L8Ya9NK{lPFtIqv;Tjn`*iHo#Gc)4vF|)BV zai&Ntoc!oLTd(iV^80%0jZw6S4WdUZ6E{SRnph(qh*jeGS6@i;1;$=USBMYF6Vgx8 zc>jx(7F#4@ED^B>I}$+@!ei@LX=GzGO>0!CFwcI@CeGer8Q`pfYjj>&lkq8PZK14n z<0J48c$!>dZC!w|m#Tt6BR%9c<3%Gung%3>fRCY1dD}XfOfGU5IuwBl0!@J#-%isV z=%lcn@=ls{b~jTxAtPMkOvT3k`*!BObKwKzP8hF6urA{R*BHxBg(@`iUJ-ZwQY&A_ z{s+mqFtQO6AxCka@e_3S!th>&eXZt>@!5Fv_vTJJO@`J4G}Pv)rg`IgwrVYu+2iF@ tIFDifDnEwqpD&Jciw;Jnw3RY4>DH`icA0i literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.po b/vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.po new file mode 100644 index 0000000000..500f7e4979 --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/qttest2_de.po @@ -0,0 +1,36 @@ +# German translations for hello-cplusplus-qt package. +# Copyright (C) 2005 Yoyodyne, Inc. +# This file is distributed under the same license as the hello-cplusplus-qt package. +# Bruno Haible , 2005. +# +msgid "" +msgstr "" +"Project-Id-Version: hello-cplusplus-qt 0\n" +"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n" +"POT-Creation-Date: 2003-10-20 10:14+0200\n" +"PO-Revision-Date: 2003-10-20 10:13+0200\n" +"Last-Translator: Bruno Haible \n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: main.cc:17 +msgctxt "Menu" +msgid "File" +msgstr "Datei" + +#: main.cc:19 +msgctxt "Menu" +msgid "Edit" +msgstr "Bearbeiten" + +#: main.cc:21 +msgctxt "Menu" +msgid "Help" +msgstr "" + +#: data.cc:45 +msgctxt "Database" +msgid "File" +msgstr "Archiv" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.mo b/vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.mo new file mode 100644 index 0000000000000000000000000000000000000000..fe0b210f5abdf0721bc4a2f42ec06139464ad1cc GIT binary patch literal 624 zcmZvZ&2AGh5XYA<3HHbp4s&VM3Qh2Cf)qB{616lUQlhR(1XsMf#xZj2#r7g9l`~Jk zgP`7d6t27m18}aK-l6)r* z#QbiOJR~p4Q+am|xrEZay>}`EVW7T(x#Q-KRURs3xN8TAaLzh7$OcK}>?hF#T(tO3 zIIpZJU?a4)*mS!05&Rs0rI(_!F5t>b)#7O{J+5s5buUqx9;KjOU&D;zfps#iF4lPF z1P^o|zUDy`kTW*KjA70oV?{Q4#;8ItV Tw_tduJiqyO()G>Hb~ot1XMe1Z literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.po b/vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.po new file mode 100644 index 0000000000..d90ab8a61b --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/qttest_pl.po @@ -0,0 +1,26 @@ +# Polish translations for hello-cplusplus-qt package. +# Copyright (C) 2003 Yoyodyne, Inc. +# This file is distributed under the same license as the hello-cplusplus-qt package. +# Bruno Haible , 2003. +# +msgid "" +msgstr "" +"Project-Id-Version: hello-cplusplus-qt 0\n" +"Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n" +"POT-Creation-Date: 2003-10-20 10:14+0200\n" +"PO-Revision-Date: 2003-10-20 10:13+0200\n" +"Last-Translator: Bruno Haible \n" +"Language-Team: Polish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" + +#: hello.cc:45 +msgid "Written by Franois Pinard." +msgstr "Program napisa Franois Pinard." + +#: hello.cc:52 +msgid "error %1." +msgstr "bd %1." diff --git a/vendor/github.com/chai2010/gettext-go/testdata/test.mo b/vendor/github.com/chai2010/gettext-go/testdata/test.mo new file mode 100644 index 0000000000000000000000000000000000000000..cb2d6cfaf28ee5418169557a4d7d405747f1e742 GIT binary patch literal 493 zcmaiv&q~8U5XPe@LXMt2>`|#3lNM=NtBA2m!D_7W&rQ~3Ojgp}vbz!W9Xtwx2f@2f z;d^-N!MAWi4#AVd{MZ@xn~$0A<%JgqV;)&S){#Y|k5myMRey>+&pFN|zDvjqYs^1U zc{s7K1vrsbcaT^*Oz8z^Ba~z$24meZOqI5DWHOP`y)rX2R*bmONlZfx+@g~1a|?{L z{UD%08xu?BcUnFM3h5Z`MdjG&I+f0Elc3A|&6eLo-4QpI#+pl0aH}*UNzMgY`lMe~ zCNE9S7jjmez7Bn5mM5RJzjP!1Vb+70lNYM2?k%ClM d5_p?l7k}!Oo~cqN5Z1Pg)V@yq^=ZpOIlu6Ni=qGk literal 0 HcmV?d00001 diff --git a/vendor/github.com/chai2010/gettext-go/testdata/test.po b/vendor/github.com/chai2010/gettext-go/testdata/test.po new file mode 100644 index 0000000000..81c6221d0f --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/test.po @@ -0,0 +1,38 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Test\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-12-12 20:03+0000\n" +"PO-Revision-Date: 2013-12-02 17:05+0800\n" +"Last-Translator: chai2010 \n" +"Language-Team: chai2010(团队) \n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.7\n" +"X-Poedit-SourceCharset: UTF-8\n" + +msgid "Title" +msgstr "Título" + +# test: bad comment +# test: good comment +#. test: extracted-comments +#: src/test.cc:9527 +#, fuzzy +#| msgctxt "" +#| "previous-context-1\n" +#| "previous-context-2" +#| msgid "" +#| "previous-untranslated-string\n" +#| "previous-untranslated-string-2" +msgid "%d topic" +msgid_plural "%d topics" +msgstr[0] "%d tema" +msgstr[1] "%d temas" diff --git a/vendor/github.com/chai2010/gettext-go/testdata/xg-c-1.ok.po b/vendor/github.com/chai2010/gettext-go/testdata/xg-c-1.ok.po new file mode 100644 index 0000000000..af5a5fdcbc --- /dev/null +++ b/vendor/github.com/chai2010/gettext-go/testdata/xg-c-1.ok.po @@ -0,0 +1,727 @@ +#, c-format, no-wrap +msgid "" +"Copyright (C) %s Free Software Foundation, Inc.\n" +"License GPLv3+: GNU GPL version 3 or later \n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +msgstr "" + +#, c-format +msgid "Written by %s.\n" +msgstr "" + +#, c-format +msgid "%s and %s are mutually exclusive" +msgstr "" + +msgid "--join-existing cannot be used when output is written to stdout" +msgstr "" + +msgid "xgettext cannot work without keywords to look for" +msgstr "" + +msgid "no input file given" +msgstr "" + +#, c-format +msgid "" +"Cannot convert from \"%s\" to \"%s\". %s relies on iconv(), and iconv() does " +"not support this conversion." +msgstr "" + +#, c-format +msgid "" +"Cannot convert from \"%s\" to \"%s\". %s relies on iconv(). This version was " +"built without iconv()." +msgstr "" + +#, c-format +msgid "warning: file '%s' extension '%s' is unknown; will try C" +msgstr "" + +#, c-format +msgid "Try '%s --help' for more information.\n" +msgstr "" + +#, c-format +msgid "Usage: %s [OPTION] [INPUTFILE]...\n" +msgstr "" + +#, c-format +msgid "Extract translatable strings from given input files.\n" +msgstr "" + +#, c-format, no-wrap +msgid "" +"Mandatory arguments to long options are mandatory for short options too.\n" +"Similarly for optional arguments.\n" +msgstr "" + +#, c-format +msgid "Input file location:\n" +msgstr "" + +#, c-format +msgid " INPUTFILE ... input files\n" +msgstr "" + +#, c-format +msgid " -f, --files-from=FILE get list of input files from FILE\n" +msgstr "" + +#, c-format +msgid "" +" -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n" +msgstr "" + +#, c-format +msgid "If input file is -, standard input is read.\n" +msgstr "" + +#, c-format +msgid "Output file location:\n" +msgstr "" + +#, c-format +msgid "" +" -d, --default-domain=NAME use NAME.po for output (instead of messages." +"po)\n" +msgstr "" + +#, c-format +msgid " -o, --output=FILE write output to specified file\n" +msgstr "" + +#, c-format +msgid "" +" -p, --output-dir=DIR output files will be placed in directory DIR\n" +msgstr "" + +#, c-format +msgid "If output file is -, output is written to standard output.\n" +msgstr "" + +#, c-format +msgid "Choice of input file language:\n" +msgstr "" + +#, c-format +msgid "" +" -L, --language=NAME recognise the specified language\n" +" (C, C++, ObjectiveC, PO, Shell, Python, " +"Lisp,\n" +" EmacsLisp, librep, Scheme, Smalltalk, Java,\n" +" JavaProperties, C#, awk, YCP, Tcl, Perl, " +"PHP,\n" +" GCC-source, NXStringTable, RST, Glade, Lua,\n" +" JavaScript, Vala)\n" +msgstr "" + +#, c-format +msgid " -C, --c++ shorthand for --language=C++\n" +msgstr "" + +#, c-format +msgid "" +"By default the language is guessed depending on the input file name " +"extension.\n" +msgstr "" + +#, c-format +msgid "Input file interpretation:\n" +msgstr "" + +#, c-format +msgid "" +" --from-code=NAME encoding of input files\n" +" (except for Python, Tcl, Glade)\n" +msgstr "" + +#, c-format +msgid "By default the input files are assumed to be in ASCII.\n" +msgstr "" + +#, c-format +msgid "Operation mode:\n" +msgstr "" + +#, c-format +msgid " -j, --join-existing join messages with existing file\n" +msgstr "" + +#, c-format +msgid " -x, --exclude-file=FILE.po entries from FILE.po are not extracted\n" +msgstr "" + +#, c-format +msgid "" +" -cTAG, --add-comments=TAG place comment blocks starting with TAG and\n" +" preceding keyword lines in output file\n" +" -c, --add-comments place all comment blocks preceding keyword " +"lines\n" +" in output file\n" +msgstr "" + +#, c-format +msgid "Language specific options:\n" +msgstr "" + +#, c-format +msgid " -a, --extract-all extract all strings\n" +msgstr "" + +#, c-format +msgid "" +" (only languages C, C++, ObjectiveC, Shell,\n" +" Python, Lisp, EmacsLisp, librep, Scheme, " +"Java,\n" +" C#, awk, Tcl, Perl, PHP, GCC-source, Glade,\n" +" Lua, JavaScript, Vala)\n" +msgstr "" + +#, c-format +msgid "" +" -kWORD, --keyword=WORD look for WORD as an additional keyword\n" +" -k, --keyword do not to use default keywords\n" +msgstr "" + +#, c-format +msgid "" +" --flag=WORD:ARG:FLAG additional flag for strings inside the " +"argument\n" +" number ARG of keyword WORD\n" +msgstr "" + +#, c-format +msgid "" +" (only languages C, C++, ObjectiveC, Shell,\n" +" Python, Lisp, EmacsLisp, librep, Scheme, " +"Java,\n" +" C#, awk, YCP, Tcl, Perl, PHP, GCC-source,\n" +" Lua, JavaScript, Vala)\n" +msgstr "" + +#, c-format +msgid " -T, --trigraphs understand ANSI C trigraphs for input\n" +msgstr "" + +#, c-format +msgid " (only languages C, C++, ObjectiveC)\n" +msgstr "" + +#, c-format +msgid " --qt recognize Qt format strings\n" +msgstr "" + +#, c-format +msgid " (only language C++)\n" +msgstr "" + +#, c-format +msgid " --kde recognize KDE 4 format strings\n" +msgstr "" + +#, c-format +msgid " --boost recognize Boost format strings\n" +msgstr "" + +#, c-format +msgid "" +" --debug more detailed formatstring recognition result\n" +msgstr "" + +#, c-format +msgid "Output details:\n" +msgstr "" + +#, c-format +msgid "" +" --color use colors and other text attributes always\n" +" --color=WHEN use colors and other text attributes if WHEN.\n" +" WHEN may be 'always', 'never', 'auto', or " +"'html'.\n" +msgstr "" + +#, c-format +msgid " --style=STYLEFILE specify CSS style rule file for --color\n" +msgstr "" + +#, c-format +msgid "" +" -e, --no-escape do not use C escapes in output (default)\n" +msgstr "" + +#, c-format +msgid "" +" -E, --escape use C escapes in output, no extended chars\n" +msgstr "" + +#, c-format +msgid " --force-po write PO file even if empty\n" +msgstr "" + +#, c-format +msgid " -i, --indent write the .po file using indented style\n" +msgstr "" + +#, c-format +msgid " --no-location do not write '#: filename:line' lines\n" +msgstr "" + +#, c-format +msgid "" +" -n, --add-location generate '#: filename:line' lines (default)\n" +msgstr "" + +#, c-format +msgid "" +" --strict write out strict Uniforum conforming .po file\n" +msgstr "" + +#, c-format +msgid " --properties-output write out a Java .properties file\n" +msgstr "" + +#, c-format +msgid "" +" --stringtable-output write out a NeXTstep/GNUstep .strings file\n" +msgstr "" + +#, c-format +msgid " -w, --width=NUMBER set output page width\n" +msgstr "" + +#, c-format +msgid "" +" --no-wrap do not break long message lines, longer than\n" +" the output page width, into several lines\n" +msgstr "" + +#, c-format +msgid " -s, --sort-output generate sorted output\n" +msgstr "" + +#, c-format +msgid " -F, --sort-by-file sort output by file location\n" +msgstr "" + +#, c-format +msgid "" +" --omit-header don't write header with 'msgid \"\"' entry\n" +msgstr "" + +#, c-format +msgid " --copyright-holder=STRING set copyright holder in output\n" +msgstr "" + +#, c-format +msgid "" +" --foreign-user omit FSF copyright in output for foreign user\n" +msgstr "" + +#, c-format +msgid " --package-name=PACKAGE set package name in output\n" +msgstr "" + +#, c-format +msgid " --package-version=VERSION set package version in output\n" +msgstr "" + +#, c-format +msgid "" +" --msgid-bugs-address=EMAIL@ADDRESS set report address for msgid bugs\n" +msgstr "" + +#, c-format +msgid "" +" -m[STRING], --msgstr-prefix[=STRING] use STRING or \"\" as prefix for " +"msgstr\n" +" values\n" +msgstr "" + +#, c-format +msgid "" +" -M[STRING], --msgstr-suffix[=STRING] use STRING or \"\" as suffix for " +"msgstr\n" +" values\n" +msgstr "" + +#, c-format +msgid "Informative output:\n" +msgstr "" + +#, c-format +msgid " -h, --help display this help and exit\n" +msgstr "" + +#, c-format +msgid " -V, --version output version information and exit\n" +msgstr "" + +msgid "Report bugs to .\n" +msgstr "" + +msgid "this file may not contain domain directives" +msgstr "" + +#, c-format +msgid "" +"A --flag argument doesn't have the ::[pass-] syntax: " +"%s" +msgstr "" + +msgid "standard input" +msgstr "" + +#, c-format +msgid "error while opening \"%s\" for reading" +msgstr "" + +#, c-format +msgid "Non-ASCII character at %s%s." +msgstr "" + +#, c-format +msgid "Non-ASCII comment at or before %s%s." +msgstr "" + +#, c-format +msgid "Non-ASCII string at %s%s." +msgstr "" + +msgid "Please specify the source encoding through --from-code." +msgstr "" + +#, c-format +msgid "%s%s: warning: " +msgstr "" + +#, c-format +msgid "" +"Although being used in a format string position, the %s is not a valid %s " +"format string. Reason: %s\n" +msgstr "" + +#, c-format +msgid "" +"Although declared as such, the %s is not a valid %s format string. Reason: " +"%s\n" +msgstr "" + +#, c-format +msgid "" +"'%s' format string with unnamed arguments cannot be properly localized:\n" +"The translator cannot reorder the arguments.\n" +"Please consider using a format string with named arguments,\n" +"and a mapping instead of a tuple for the arguments.\n" +msgstr "" + +msgid "" +"Empty msgid. It is reserved by GNU gettext:\n" +"gettext(\"\") returns the header entry with\n" +"meta information, not the empty string.\n" +msgstr "" + +#, c-format +msgid "ambiguous argument specification for keyword '%.*s'" +msgstr "" + +#, c-format +msgid "warning: missing context for keyword '%.*s'" +msgstr "" + +#, c-format +msgid "warning: missing context for plural argument of keyword '%.*s'" +msgstr "" + +msgid "context mismatch between singular and plural form" +msgstr "" + +msgid "warning: " +msgstr "" + +msgid "" +"The option --msgid-bugs-address was not specified.\n" +"If you are using a 'Makevars' file, please specify\n" +"the MSGID_BUGS_ADDRESS variable there; otherwise please\n" +"specify an --msgid-bugs-address command line option.\n" +msgstr "" + +#, c-format +msgid "language '%s' unknown" +msgstr "" + +#, c-format +msgid "the argument to %s should be a single punctuation character" +msgstr "" + +#, c-format +msgid "invalid endianness: %s" +msgstr "" + +#, c-format +msgid "%s requires a \"-d directory\" specification" +msgstr "" + +#, c-format +msgid "%s requires a \"-l locale\" specification" +msgstr "" + +#, c-format +msgid "%s is only valid with %s or %s" +msgstr "" + +#, c-format +msgid "%s is only valid with %s, %s or %s" +msgstr "" + +#, c-format +msgid "found %d fatal error" +msgid_plural "found %d fatal errors" +msgstr[0] "" +msgstr[1] "" + +#, c-format +msgid "%s: " +msgstr "" + +#, c-format +msgid "%d translated message" +msgid_plural "%d translated messages" +msgstr[0] "" +msgstr[1] "" + +#, c-format +msgid ", %d fuzzy translation" +msgid_plural ", %d fuzzy translations" +msgstr[0] "" +msgstr[1] "" + +#, c-format +msgid ", %d untranslated message" +msgid_plural ", %d untranslated messages" +msgstr[0] "" +msgstr[1] "" + +#, c-format +msgid "Usage: %s [OPTION] filename.po ...\n" +msgstr "" + +#, c-format +msgid "Generate binary message catalog from textual translation description.\n" +msgstr "" + +#, c-format +msgid " filename.po ... input files\n" +msgstr "" + +#, c-format +msgid "" +" -j, --java Java mode: generate a Java ResourceBundle " +"class\n" +msgstr "" + +#, c-format +msgid "" +" --java2 like --java, and assume Java2 (JDK 1.2 or " +"higher)\n" +msgstr "" + +#, c-format +msgid " --csharp C# mode: generate a .NET .dll file\n" +msgstr "" + +#, c-format +msgid "" +" --csharp-resources C# resources mode: generate a .NET .resources " +"file\n" +msgstr "" + +#, c-format +msgid "" +" --tcl Tcl mode: generate a tcl/msgcat .msg file\n" +msgstr "" + +#, c-format +msgid " --qt Qt mode: generate a Qt .qm file\n" +msgstr "" + +#, c-format +msgid " -o, --output-file=FILE write output to specified file\n" +msgstr "" + +#, c-format +msgid " --strict enable strict Uniforum mode\n" +msgstr "" + +#, c-format +msgid "Output file location in Java mode:\n" +msgstr "" + +#, c-format +msgid " -r, --resource=RESOURCE resource name\n" +msgstr "" + +#, c-format +msgid "" +" -l, --locale=LOCALE locale name, either language or " +"language_COUNTRY\n" +msgstr "" + +#, c-format +msgid "" +" -d DIRECTORY base directory of classes directory hierarchy\n" +msgstr "" + +#, c-format +msgid "" +"The class name is determined by appending the locale name to the resource " +"name,\n" +"separated with an underscore. The -d option is mandatory. The class is\n" +"written under the specified directory.\n" +msgstr "" + +#, c-format +msgid "Output file location in C# mode:\n" +msgstr "" + +#, c-format +msgid "" +" -d DIRECTORY base directory for locale dependent .dll " +"files\n" +msgstr "" + +#, c-format +msgid "" +"The -l and -d options are mandatory. The .dll file is written in a\n" +"subdirectory of the specified directory whose name depends on the locale.\n" +msgstr "" + +#, c-format +msgid "Output file location in Tcl mode:\n" +msgstr "" + +#, c-format +msgid " -d DIRECTORY base directory of .msg message catalogs\n" +msgstr "" + +#, c-format +msgid "" +"The -l and -d options are mandatory. The .msg file is written in the\n" +"specified directory.\n" +msgstr "" + +#, c-format +msgid "Input file syntax:\n" +msgstr "" + +#, c-format +msgid "" +" -P, --properties-input input files are in Java .properties syntax\n" +msgstr "" + +#, c-format +msgid "" +" --stringtable-input input files are in NeXTstep/GNUstep .strings\n" +" syntax\n" +msgstr "" + +#, c-format +msgid "" +" -c, --check perform all the checks implied by\n" +" --check-format, --check-header, --check-" +"domain\n" +msgstr "" + +#, c-format +msgid " --check-format check language dependent format strings\n" +msgstr "" + +#, c-format +msgid "" +" --check-header verify presence and contents of the header " +"entry\n" +msgstr "" + +#, c-format +msgid "" +" --check-domain check for conflicts between domain directives\n" +" and the --output-file option\n" +msgstr "" + +#, c-format +msgid "" +" -C, --check-compatibility check that GNU msgfmt behaves like X/Open " +"msgfmt\n" +msgstr "" + +#, c-format +msgid "" +" --check-accelerators[=CHAR] check presence of keyboard accelerators " +"for\n" +" menu items\n" +msgstr "" + +#, c-format +msgid " -f, --use-fuzzy use fuzzy entries in output\n" +msgstr "" + +#, c-format +msgid "" +" -a, --alignment=NUMBER align strings to NUMBER bytes (default: %d)\n" +msgstr "" + +#, c-format +msgid "" +" --endianness=BYTEORDER write out 32-bit numbers in the given byte " +"order\n" +" (big or little, default depends on " +"platform)\n" +msgstr "" + +#, c-format +msgid "" +" --no-hash binary file will not include the hash table\n" +msgstr "" + +#, c-format +msgid " --statistics print statistics about translations\n" +msgstr "" + +#, c-format +msgid " -v, --verbose increase verbosity level\n" +msgstr "" + +msgid "warning: PO file header missing or invalid\n" +msgstr "" + +msgid "warning: charset conversion will not work\n" +msgstr "" + +msgid "warning: PO file header fuzzy\n" +msgstr "" + +msgid "warning: older versions of msgfmt will give an error on this\n" +msgstr "" + +#, c-format +msgid "domain name \"%s\" not suitable as file name" +msgstr "" + +#, c-format +msgid "domain name \"%s\" not suitable as file name: will use prefix" +msgstr "" + +#, c-format +msgid "'domain %s' directive ignored" +msgstr "" + +msgid "empty 'msgstr' entry ignored" +msgstr "" + +msgid "fuzzy 'msgstr' entry ignored" +msgstr "" + +#, c-format +msgid "%s: warning: source file contains fuzzy translation" +msgstr "" diff --git a/vendor/github.com/daviddengcn/go-colortext/.gitignore b/vendor/github.com/daviddengcn/go-colortext/.gitignore new file mode 100644 index 0000000000..00268614f0 --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/daviddengcn/go-colortext/LICENSE b/vendor/github.com/daviddengcn/go-colortext/LICENSE new file mode 100644 index 0000000000..62ca9ee2e4 --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/LICENSE @@ -0,0 +1,54 @@ +BSD License +=========== + +Copyright (c) 2016, David Deng +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of go-colortext nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +MIT License +=========== + +Copyright (c) 2016 David Deng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/daviddengcn/go-colortext/README.md b/vendor/github.com/daviddengcn/go-colortext/README.md new file mode 100644 index 0000000000..f7feca8728 --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/README.md @@ -0,0 +1,21 @@ +go-colortext package [![GoSearch](http://go-search.org/badge?id=github.com%2Fdaviddengcn%2Fgo-colortext)](http://go-search.org/view?id=github.com%2Fdaviddengcn%2Fgo-colortext) +==================== + +This is a package to change the color of the text and background in the console, working both under Windows and other systems. + +Under Windows, the console APIs are used. Otherwise, ANSI texts are output. + +Docs: http://godoc.org/github.com/daviddengcn/go-colortext ([packages that import ct](http://go-search.org/view?id=github.com%2fdaviddengcn%2fgo-colortext)) + +Usage: +```go +ct.Foreground(Green, false) +fmt.Println("Green text starts here...") +ct.ChangeColor(Red, true, White, false) +fmt.Println(...) +ct.ResetColor() +``` + +LICENSE +======= +BSD/MIT license diff --git a/vendor/github.com/daviddengcn/go-colortext/ct.go b/vendor/github.com/daviddengcn/go-colortext/ct.go new file mode 100644 index 0000000000..d93bc882f8 --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/ct.go @@ -0,0 +1,45 @@ +/* +ct package provides functions to change the color of console text. + +Under windows platform, the Console API is used. Under other systems, ANSI text mode is used. +*/ +package ct + +// Color is the type of color to be set. +type Color int + +const ( + // No change of color + None = Color(iota) + Black + Red + Green + Yellow + Blue + Magenta + Cyan + White +) + +// ResetColor resets the foreground and background to original colors +func ResetColor() { + resetColor() +} + +// ChangeColor sets the foreground and background colors. If the value of the color is None, +// the corresponding color keeps unchanged. +// If fgBright or bgBright is set true, corresponding color use bright color. bgBright may be +// ignored in some OS environment. +func ChangeColor(fg Color, fgBright bool, bg Color, bgBright bool) { + changeColor(fg, fgBright, bg, bgBright) +} + +// Foreground changes the foreground color. +func Foreground(cl Color, bright bool) { + ChangeColor(cl, bright, None, false) +} + +// Background changes the background color. +func Background(cl Color, bright bool) { + ChangeColor(None, false, cl, bright) +} diff --git a/vendor/github.com/daviddengcn/go-colortext/ct_ansi.go b/vendor/github.com/daviddengcn/go-colortext/ct_ansi.go new file mode 100644 index 0000000000..dc2eb88cc8 --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/ct_ansi.go @@ -0,0 +1,48 @@ +// +build !windows + +package ct + +import ( + "fmt" + "os" + "strconv" +) + +func isDumbTerm() bool { + return os.Getenv("TERM") == "dumb" +} + +func resetColor() { + if isDumbTerm() { + return + } + fmt.Print("\x1b[0m") +} + +func ansiText(fg Color, fgBright bool, bg Color, bgBright bool) string { + if fg == None && bg == None { + return "" + } + s := []byte("\x1b[0") + if fg != None { + s = strconv.AppendUint(append(s, ";"...), 30+(uint64)(fg-Black), 10) + if fgBright { + s = append(s, ";1"...) + } + } + if bg != None { + s = strconv.AppendUint(append(s, ";"...), 40+(uint64)(bg-Black), 10) + } + s = append(s, "m"...) + return string(s) +} + +func changeColor(fg Color, fgBright bool, bg Color, bgBright bool) { + if isDumbTerm() { + return + } + if fg == None && bg == None { + return + } + fmt.Print(ansiText(fg, fgBright, bg, bgBright)) +} diff --git a/vendor/github.com/daviddengcn/go-colortext/ct_ansi_test.go b/vendor/github.com/daviddengcn/go-colortext/ct_ansi_test.go new file mode 100644 index 0000000000..d6588a7a6c --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/ct_ansi_test.go @@ -0,0 +1,18 @@ +// +build !windows + +package ct + +import ( + "testing" + + "github.com/golangplus/testing/assert" +) + +func TestAnsiText(t *testing.T) { + assert.Equal(t, "ansiText", ansiText(None, false, None, false), "") + assert.Equal(t, "ansiText", ansiText(Red, false, None, false), "\x1b[0;31m") + assert.Equal(t, "ansiText", ansiText(Red, true, None, false), "\x1b[0;31;1m") + assert.Equal(t, "ansiText", ansiText(None, false, Green, false), "\x1b[0;42m") + assert.Equal(t, "ansiText", ansiText(Red, false, Green, false), "\x1b[0;31;42m") + assert.Equal(t, "ansiText", ansiText(Red, true, Green, false), "\x1b[0;31;1;42m") +} diff --git a/vendor/github.com/daviddengcn/go-colortext/ct_test.go b/vendor/github.com/daviddengcn/go-colortext/ct_test.go new file mode 100644 index 0000000000..9ba0438816 --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/ct_test.go @@ -0,0 +1,82 @@ +package ct + +import ( + "fmt" + "testing" +) + +func TestChangeColor(t *testing.T) { + defer ResetColor() + fmt.Println("Normal text...") + text := "This is an demo of using ChangeColor to output colorful texts" + i := 1 + for _, c := range text { + ChangeColor(Color(i/2%8)+Black, i%2 == 1, Color((i+2)/2%8)+Black, false) + fmt.Print(string(c)) + i++ + } + fmt.Println() + ChangeColor(Red, true, White, false) + fmt.Println("Before reset.") + ChangeColor(Red, false, White, true) + fmt.Println("Before reset.") + ResetColor() + fmt.Println("After reset.") + fmt.Println("After reset.") +} + +func TestForeground(t *testing.T) { + ResetColor() + defer ResetColor() + + fmt.Println("Please check the words under the following text shows with the corresponding front color:") + + colorToText := [...]string{ + Black: "black", + Red: "red", + Green: "green", + Yellow: "yellow", + Blue: "blue", + Magenta: "magenta", + Cyan: "cyan", + White: "white", + } + for i, txt := range colorToText { + cl := Color(i) + if cl != None { + Foreground(cl, false) + fmt.Print(txt, ",") + Foreground(cl, true) + fmt.Print(txt, ",") + } + } + fmt.Println() +} + +func TestBackground(t *testing.T) { + ResetColor() + defer ResetColor() + + fmt.Println("Please check the words under the following text shows with the corresponding background color:") + + colorToText := [...]string{ + Black: "black", + Red: "red", + Green: "green", + Yellow: "yellow", + Blue: "blue", + Magenta: "magenta", + Cyan: "cyan", + White: "white", + } + for i, txt := range colorToText { + cl := Color(i) + if cl != None { + Background(cl, false) + fmt.Print(txt, ",") + Background(cl, true) + fmt.Print(txt, ",") + } + } + fmt.Println() +} diff --git a/vendor/github.com/daviddengcn/go-colortext/ct_win.go b/vendor/github.com/daviddengcn/go-colortext/ct_win.go new file mode 100644 index 0000000000..2b267f458c --- /dev/null +++ b/vendor/github.com/daviddengcn/go-colortext/ct_win.go @@ -0,0 +1,133 @@ +// +build windows + +package ct + +import ( + "syscall" + "unsafe" +) + +var fg_colors = []uint16{ + 0, + 0, + foreground_red, + foreground_green, + foreground_red | foreground_green, + foreground_blue, + foreground_red | foreground_blue, + foreground_green | foreground_blue, + foreground_red | foreground_green | foreground_blue} + +var bg_colors = []uint16{ + 0, + 0, + background_red, + background_green, + background_red | background_green, + background_blue, + background_red | background_blue, + background_green | background_blue, + background_red | background_green | background_blue} + +const ( + foreground_blue = uint16(0x0001) + foreground_green = uint16(0x0002) + foreground_red = uint16(0x0004) + foreground_intensity = uint16(0x0008) + background_blue = uint16(0x0010) + background_green = uint16(0x0020) + background_red = uint16(0x0040) + background_intensity = uint16(0x0080) + + foreground_mask = foreground_blue | foreground_green | foreground_red | foreground_intensity + background_mask = background_blue | background_green | background_red | background_intensity +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + + procGetStdHandle = kernel32.NewProc("GetStdHandle") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + + hStdout uintptr + initScreenInfo *console_screen_buffer_info +) + +func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { + ret, _, _ := procSetConsoleTextAttribute.Call( + hConsoleOutput, + uintptr(wAttributes)) + return ret != 0 +} + +type coord struct { + X, Y int16 +} + +type small_rect struct { + Left, Top, Right, Bottom int16 +} + +type console_screen_buffer_info struct { + DwSize coord + DwCursorPosition coord + WAttributes uint16 + SrWindow small_rect + DwMaximumWindowSize coord +} + +func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *console_screen_buffer_info { + var csbi console_screen_buffer_info + if ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi))); ret == 0 { + return nil + } + return &csbi +} + +const ( + std_output_handle = uint32(-11 & 0xFFFFFFFF) +) + +func init() { + kernel32 := syscall.NewLazyDLL("kernel32.dll") + + procGetStdHandle = kernel32.NewProc("GetStdHandle") + + hStdout, _, _ = procGetStdHandle.Call(uintptr(std_output_handle)) + + initScreenInfo = getConsoleScreenBufferInfo(hStdout) + + syscall.LoadDLL("") +} + +func resetColor() { + if initScreenInfo == nil { // No console info - Ex: stdout redirection + return + } + setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes) +} + +func changeColor(fg Color, fgBright bool, bg Color, bgBright bool) { + attr := uint16(0) + if fg == None || bg == None { + cbufinfo := getConsoleScreenBufferInfo(hStdout) + if cbufinfo == nil { // No console info - Ex: stdout redirection + return + } + attr = cbufinfo.WAttributes + } + if fg != None { + attr = attr & ^foreground_mask | fg_colors[fg] + if fgBright { + attr |= foreground_intensity + } + } + if bg != None { + attr = attr & ^background_mask | bg_colors[bg] + if bgBright { + attr |= background_intensity + } + } + setConsoleTextAttribute(hStdout, attr) +} diff --git a/vendor/github.com/docker/spdystream/CONTRIBUTING.md b/vendor/github.com/docker/spdystream/CONTRIBUTING.md new file mode 100644 index 0000000000..d4eddcc539 --- /dev/null +++ b/vendor/github.com/docker/spdystream/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing to SpdyStream + +Want to hack on spdystream? Awesome! Here are instructions to get you +started. + +SpdyStream is a part of the [Docker](https://docker.io) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read +[Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md). + +Happy hacking! diff --git a/vendor/github.com/docker/spdystream/LICENSE b/vendor/github.com/docker/spdystream/LICENSE new file mode 100644 index 0000000000..9e4bd4dbee --- /dev/null +++ b/vendor/github.com/docker/spdystream/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014-2015 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/spdystream/LICENSE.docs b/vendor/github.com/docker/spdystream/LICENSE.docs new file mode 100644 index 0000000000..e26cd4fc8e --- /dev/null +++ b/vendor/github.com/docker/spdystream/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/vendor/github.com/docker/spdystream/MAINTAINERS b/vendor/github.com/docker/spdystream/MAINTAINERS new file mode 100644 index 0000000000..14e263325c --- /dev/null +++ b/vendor/github.com/docker/spdystream/MAINTAINERS @@ -0,0 +1,28 @@ +# Spdystream maintainers file +# +# This file describes who runs the docker/spdystream project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "dmcgowan", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + + [people.dmcgowan] + Name = "Derek McGowan" + Email = "derek@docker.com" + GitHub = "dmcgowan" diff --git a/vendor/github.com/docker/spdystream/README.md b/vendor/github.com/docker/spdystream/README.md new file mode 100644 index 0000000000..11cccd0a09 --- /dev/null +++ b/vendor/github.com/docker/spdystream/README.md @@ -0,0 +1,77 @@ +# SpdyStream + +A multiplexed stream library using spdy + +## Usage + +Client example (connecting to mirroring server without auth) + +```go +package main + +import ( + "fmt" + "github.com/docker/spdystream" + "net" + "net/http" +) + +func main() { + conn, err := net.Dial("tcp", "localhost:8080") + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, false) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.NoOpStreamHandler) + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + panic(err) + } + + stream.Wait() + + fmt.Fprint(stream, "Writing to stream") + + buf := make([]byte, 25) + stream.Read(buf) + fmt.Println(string(buf)) + + stream.Close() +} +``` + +Server example (mirroring server without auth) + +```go +package main + +import ( + "github.com/docker/spdystream" + "net" +) + +func main() { + listener, err := net.Listen("tcp", "localhost:8080") + if err != nil { + panic(err) + } + for { + conn, err := listener.Accept() + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, true) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.MirrorStreamHandler) + } +} +``` + +## Copyright and license + +Copyright © 2014-2015 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. diff --git a/vendor/github.com/docker/spdystream/connection.go b/vendor/github.com/docker/spdystream/connection.go new file mode 100644 index 0000000000..6031a0db1a --- /dev/null +++ b/vendor/github.com/docker/spdystream/connection.go @@ -0,0 +1,958 @@ +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream/spdy" +) + +var ( + ErrInvalidStreamId = errors.New("Invalid stream id") + ErrTimeout = errors.New("Timeout occured") + ErrReset = errors.New("Stream reset") + ErrWriteClosedStream = errors.New("Write on closed stream") +) + +const ( + FRAME_WORKERS = 5 + QUEUE_SIZE = 50 +) + +type StreamHandler func(stream *Stream) + +type AuthHandler func(header http.Header, slot uint8, parent uint32) bool + +type idleAwareFramer struct { + f *spdy.Framer + conn *Connection + writeLock sync.Mutex + resetChan chan struct{} + setTimeoutLock sync.Mutex + setTimeoutChan chan time.Duration + timeout time.Duration +} + +func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer { + iaf := &idleAwareFramer{ + f: framer, + resetChan: make(chan struct{}, 2), + // setTimeoutChan needs to be buffered to avoid deadlocks when calling setIdleTimeout at about + // the same time the connection is being closed + setTimeoutChan: make(chan time.Duration, 1), + } + return iaf +} + +func (i *idleAwareFramer) monitor() { + var ( + timer *time.Timer + expired <-chan time.Time + resetChan = i.resetChan + setTimeoutChan = i.setTimeoutChan + ) +Loop: + for { + select { + case timeout := <-i.setTimeoutChan: + i.timeout = timeout + if timeout == 0 { + if timer != nil { + timer.Stop() + } + } else { + if timer == nil { + timer = time.NewTimer(timeout) + expired = timer.C + } else { + timer.Reset(timeout) + } + } + case <-resetChan: + if timer != nil && i.timeout > 0 { + timer.Reset(i.timeout) + } + case <-expired: + i.conn.streamCond.L.Lock() + streams := i.conn.streams + i.conn.streams = make(map[spdy.StreamId]*Stream) + i.conn.streamCond.Broadcast() + i.conn.streamCond.L.Unlock() + go func() { + for _, stream := range streams { + stream.resetStream() + } + i.conn.Close() + }() + case <-i.conn.closeChan: + if timer != nil { + timer.Stop() + } + + // Start a goroutine to drain resetChan. This is needed because we've seen + // some unit tests with large numbers of goroutines get into a situation + // where resetChan fills up, at least 1 call to Write() is still trying to + // send to resetChan, the connection gets closed, and this case statement + // attempts to grab the write lock that Write() already has, causing a + // deadlock. + // + // See https://github.com/docker/spdystream/issues/49 for more details. + go func() { + for _ = range resetChan { + } + }() + + go func() { + for _ = range setTimeoutChan { + } + }() + + i.writeLock.Lock() + close(resetChan) + i.resetChan = nil + i.writeLock.Unlock() + + i.setTimeoutLock.Lock() + close(i.setTimeoutChan) + i.setTimeoutChan = nil + i.setTimeoutLock.Unlock() + + break Loop + } + } + + // Drain resetChan + for _ = range resetChan { + } +} + +func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error { + i.writeLock.Lock() + defer i.writeLock.Unlock() + if i.resetChan == nil { + return io.EOF + } + err := i.f.WriteFrame(frame) + if err != nil { + return err + } + + i.resetChan <- struct{}{} + + return nil +} + +func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) { + frame, err := i.f.ReadFrame() + if err != nil { + return nil, err + } + + // resetChan should never be closed since it is only closed + // when the connection has closed its closeChan. This closure + // only occurs after all Reads have finished + // TODO (dmcgowan): refactor relationship into connection + i.resetChan <- struct{}{} + + return frame, nil +} + +func (i *idleAwareFramer) setIdleTimeout(timeout time.Duration) { + i.setTimeoutLock.Lock() + defer i.setTimeoutLock.Unlock() + + if i.setTimeoutChan == nil { + return + } + + i.setTimeoutChan <- timeout +} + +type Connection struct { + conn net.Conn + framer *idleAwareFramer + + closeChan chan bool + goneAway bool + lastStreamChan chan<- *Stream + goAwayTimeout time.Duration + closeTimeout time.Duration + + streamLock *sync.RWMutex + streamCond *sync.Cond + streams map[spdy.StreamId]*Stream + + nextIdLock sync.Mutex + receiveIdLock sync.Mutex + nextStreamId spdy.StreamId + receivedStreamId spdy.StreamId + + pingIdLock sync.Mutex + pingId uint32 + pingChans map[uint32]chan error + + shutdownLock sync.Mutex + shutdownChan chan error + hasShutdown bool + + // for testing https://github.com/docker/spdystream/pull/56 + dataFrameHandler func(*spdy.DataFrame) error +} + +// NewConnection creates a new spdy connection from an existing +// network connection. +func NewConnection(conn net.Conn, server bool) (*Connection, error) { + framer, framerErr := spdy.NewFramer(conn, conn) + if framerErr != nil { + return nil, framerErr + } + idleAwareFramer := newIdleAwareFramer(framer) + var sid spdy.StreamId + var rid spdy.StreamId + var pid uint32 + if server { + sid = 2 + rid = 1 + pid = 2 + } else { + sid = 1 + rid = 2 + pid = 1 + } + + streamLock := new(sync.RWMutex) + streamCond := sync.NewCond(streamLock) + + session := &Connection{ + conn: conn, + framer: idleAwareFramer, + + closeChan: make(chan bool), + goAwayTimeout: time.Duration(0), + closeTimeout: time.Duration(0), + + streamLock: streamLock, + streamCond: streamCond, + streams: make(map[spdy.StreamId]*Stream), + nextStreamId: sid, + receivedStreamId: rid, + + pingId: pid, + pingChans: make(map[uint32]chan error), + + shutdownChan: make(chan error), + } + session.dataFrameHandler = session.handleDataFrame + idleAwareFramer.conn = session + go idleAwareFramer.monitor() + + return session, nil +} + +// Ping sends a ping frame across the connection and +// returns the response time +func (s *Connection) Ping() (time.Duration, error) { + pid := s.pingId + s.pingIdLock.Lock() + if s.pingId > 0x7ffffffe { + s.pingId = s.pingId - 0x7ffffffe + } else { + s.pingId = s.pingId + 2 + } + s.pingIdLock.Unlock() + pingChan := make(chan error) + s.pingChans[pid] = pingChan + defer delete(s.pingChans, pid) + + frame := &spdy.PingFrame{Id: pid} + startTime := time.Now() + writeErr := s.framer.WriteFrame(frame) + if writeErr != nil { + return time.Duration(0), writeErr + } + select { + case <-s.closeChan: + return time.Duration(0), errors.New("connection closed") + case err, ok := <-pingChan: + if ok && err != nil { + return time.Duration(0), err + } + break + } + return time.Now().Sub(startTime), nil +} + +// Serve handles frames sent from the server, including reply frames +// which are needed to fully initiate connections. Both clients and servers +// should call Serve in a separate goroutine before creating streams. +func (s *Connection) Serve(newHandler StreamHandler) { + // use a WaitGroup to wait for all frames to be drained after receiving + // go-away. + var wg sync.WaitGroup + + // Parition queues to ensure stream frames are handled + // by the same worker, ensuring order is maintained + frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS) + for i := 0; i < FRAME_WORKERS; i++ { + frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE) + + // Ensure frame queue is drained when connection is closed + go func(frameQueue *PriorityFrameQueue) { + <-s.closeChan + frameQueue.Drain() + }(frameQueues[i]) + + wg.Add(1) + go func(frameQueue *PriorityFrameQueue) { + // let the WaitGroup know this worker is done + defer wg.Done() + + s.frameHandler(frameQueue, newHandler) + }(frameQueues[i]) + } + + var ( + partitionRoundRobin int + goAwayFrame *spdy.GoAwayFrame + ) +Loop: + for { + readFrame, err := s.framer.ReadFrame() + if err != nil { + if err != io.EOF { + fmt.Errorf("frame read error: %s", err) + } else { + debugMessage("(%p) EOF received", s) + } + break + } + var priority uint8 + var partition int + switch frame := readFrame.(type) { + case *spdy.SynStreamFrame: + if s.checkStreamFrame(frame) { + priority = frame.Priority + partition = int(frame.StreamId % FRAME_WORKERS) + debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId) + s.addStreamFrame(frame) + } else { + debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId) + continue + } + case *spdy.SynReplyFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.DataFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.RstStreamFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.HeadersFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.PingFrame: + priority = 0 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + case *spdy.GoAwayFrame: + // hold on to the go away frame and exit the loop + goAwayFrame = frame + break Loop + default: + priority = 7 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + } + frameQueues[partition].Push(readFrame, priority) + } + close(s.closeChan) + + // wait for all frame handler workers to indicate they've drained their queues + // before handling the go away frame + wg.Wait() + + if goAwayFrame != nil { + s.handleGoAwayFrame(goAwayFrame) + } + + // now it's safe to close remote channels and empty s.streams + s.streamCond.L.Lock() + // notify streams that they're now closed, which will + // unblock any stream Read() calls + for _, stream := range s.streams { + stream.closeRemoteChannels() + } + s.streams = make(map[spdy.StreamId]*Stream) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) { + for { + popFrame := frameQueue.Pop() + if popFrame == nil { + return + } + + var frameErr error + switch frame := popFrame.(type) { + case *spdy.SynStreamFrame: + frameErr = s.handleStreamFrame(frame, newHandler) + case *spdy.SynReplyFrame: + frameErr = s.handleReplyFrame(frame) + case *spdy.DataFrame: + frameErr = s.dataFrameHandler(frame) + case *spdy.RstStreamFrame: + frameErr = s.handleResetFrame(frame) + case *spdy.HeadersFrame: + frameErr = s.handleHeaderFrame(frame) + case *spdy.PingFrame: + frameErr = s.handlePingFrame(frame) + case *spdy.GoAwayFrame: + frameErr = s.handleGoAwayFrame(frame) + default: + frameErr = fmt.Errorf("unhandled frame type: %T", frame) + } + + if frameErr != nil { + fmt.Errorf("frame handling error: %s", frameErr) + } + } +} + +func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 { + stream, streamOk := s.getStream(streamId) + if !streamOk { + return 7 + } + return stream.priority +} + +func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) { + var parent *Stream + if frame.AssociatedToStreamId != spdy.StreamId(0) { + parent, _ = s.getStream(frame.AssociatedToStreamId) + } + + stream := &Stream{ + streamId: frame.StreamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: frame.Headers, + finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00, + replyCond: sync.NewCond(new(sync.Mutex)), + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + } + if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 { + stream.closeRemoteChannels() + } + + s.addStream(stream) +} + +// checkStreamFrame checks to see if a stream frame is allowed. +// If the stream is invalid, then a reset frame with protocol error +// will be returned. +func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool { + s.receiveIdLock.Lock() + defer s.receiveIdLock.Unlock() + if s.goneAway { + return false + } + validationErr := s.validateStreamId(frame.StreamId) + if validationErr != nil { + go func() { + resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId) + if resetErr != nil { + fmt.Errorf("reset error: %s", resetErr) + } + }() + return false + } + return true +} + +func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error { + stream, ok := s.getStream(frame.StreamId) + if !ok { + return fmt.Errorf("Missing stream: %d", frame.StreamId) + } + + newHandler(stream) + + return nil +} + +func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error { + debugMessage("(%p) Reply frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("Reply frame gone away for %d", frame.StreamId) + // Stream has already gone away + return nil + } + if stream.replied { + // Stream has already received reply + return nil + } + stream.replied = true + + // TODO Check for error + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + close(stream.startChan) + + return nil +} + +func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already been removed + return nil + } + s.removeStream(stream) + stream.closeRemoteChannels() + + if !stream.replied { + stream.replied = true + stream.startChan <- ErrReset + close(stream.startChan) + } + + stream.finishLock.Lock() + stream.finished = true + stream.finishLock.Unlock() + + return nil +} + +func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already gone away + return nil + } + if !stream.replied { + // No reply received...Protocol error? + return nil + } + + // TODO limit headers while not blocking (use buffered chan or goroutine?) + select { + case <-stream.closeChan: + return nil + case stream.headerChan <- frame.Headers: + } + + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + return nil +} + +func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error { + debugMessage("(%p) Data frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("(%p) Data frame gone away for %d", s, frame.StreamId) + // Stream has already gone away + return nil + } + if !stream.replied { + debugMessage("(%p) Data frame not replied %d", s, frame.StreamId) + // No reply received...Protocol error? + return nil + } + + debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId) + if len(frame.Data) > 0 { + stream.dataLock.RLock() + select { + case <-stream.closeChan: + debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId) + case stream.dataChan <- frame.Data: + debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId) + } + stream.dataLock.RUnlock() + } + if (frame.Flags & spdy.DataFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + return nil +} + +func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error { + if s.pingId&0x01 != frame.Id&0x01 { + return s.framer.WriteFrame(frame) + } + pingChan, pingOk := s.pingChans[frame.Id] + if pingOk { + close(pingChan) + } + return nil +} + +func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error { + debugMessage("(%p) Go away received", s) + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + if s.lastStreamChan != nil { + stream, _ := s.getStream(frame.LastGoodStreamId) + go func() { + s.lastStreamChan <- stream + }() + } + + // Do not block frame handler waiting for closure + go s.shutdown(s.goAwayTimeout) + + return nil +} + +func (s *Connection) remoteStreamFinish(stream *Stream) { + stream.closeRemoteChannels() + + stream.finishLock.Lock() + if stream.finished { + // Stream is fully closed, cleanup + s.removeStream(stream) + } + stream.finishLock.Unlock() +} + +// CreateStream creates a new spdy stream using the parameters for +// creating the stream frame. The stream frame will be sent upon +// calling this function, however this function does not wait for +// the reply frame. If waiting for the reply is desired, use +// the stream Wait or WaitTimeout function on the stream returned +// by this function. +func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) { + // MUST synchronize stream creation (all the way to writing the frame) + // as stream IDs **MUST** increase monotonically. + s.nextIdLock.Lock() + defer s.nextIdLock.Unlock() + + streamId := s.getNextStreamId() + if streamId == 0 { + return nil, fmt.Errorf("Unable to get new stream id") + } + + stream := &Stream{ + streamId: streamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: headers, + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + } + + debugMessage("(%p) (%p) Create stream", s, stream) + + s.addStream(stream) + + return stream, s.sendStream(stream, fin) +} + +func (s *Connection) shutdown(closeTimeout time.Duration) { + // TODO Ensure this isn't called multiple times + s.shutdownLock.Lock() + if s.hasShutdown { + s.shutdownLock.Unlock() + return + } + s.hasShutdown = true + s.shutdownLock.Unlock() + + var timeout <-chan time.Time + if closeTimeout > time.Duration(0) { + timeout = time.After(closeTimeout) + } + streamsClosed := make(chan bool) + + go func() { + s.streamCond.L.Lock() + for len(s.streams) > 0 { + debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams) + s.streamCond.Wait() + } + s.streamCond.L.Unlock() + close(streamsClosed) + }() + + var err error + select { + case <-streamsClosed: + // No active streams, close should be safe + err = s.conn.Close() + case <-timeout: + // Force ungraceful close + err = s.conn.Close() + // Wait for cleanup to clear active streams + <-streamsClosed + } + + if err != nil { + duration := 10 * time.Minute + time.AfterFunc(duration, func() { + select { + case err, ok := <-s.shutdownChan: + if ok { + fmt.Errorf("Unhandled close error after %s: %s", duration, err) + } + default: + } + }) + s.shutdownChan <- err + } + close(s.shutdownChan) + + return +} + +// Closes spdy connection by sending GoAway frame and initiating shutdown +func (s *Connection) Close() error { + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + var lastStreamId spdy.StreamId + if s.receivedStreamId > 2 { + lastStreamId = s.receivedStreamId - 2 + } + + goAwayFrame := &spdy.GoAwayFrame{ + LastGoodStreamId: lastStreamId, + Status: spdy.GoAwayOK, + } + + err := s.framer.WriteFrame(goAwayFrame) + if err != nil { + return err + } + + go s.shutdown(s.closeTimeout) + + return nil +} + +// CloseWait closes the connection and waits for shutdown +// to finish. Note the underlying network Connection +// is not closed until the end of shutdown. +func (s *Connection) CloseWait() error { + closeErr := s.Close() + if closeErr != nil { + return closeErr + } + shutdownErr, ok := <-s.shutdownChan + if ok { + return shutdownErr + } + return nil +} + +// Wait waits for the connection to finish shutdown or for +// the wait timeout duration to expire. This needs to be +// called either after Close has been called or the GOAWAYFRAME +// has been received. If the wait timeout is 0, this function +// will block until shutdown finishes. If wait is never called +// and a shutdown error occurs, that error will be logged as an +// unhandled error. +func (s *Connection) Wait(waitTimeout time.Duration) error { + var timeout <-chan time.Time + if waitTimeout > time.Duration(0) { + timeout = time.After(waitTimeout) + } + + select { + case err, ok := <-s.shutdownChan: + if ok { + return err + } + case <-timeout: + return ErrTimeout + } + return nil +} + +// NotifyClose registers a channel to be called when the remote +// peer inidicates connection closure. The last stream to be +// received by the remote will be sent on the channel. The notify +// timeout will determine the duration between go away received +// and the connection being closed. +func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) { + s.goAwayTimeout = timeout + s.lastStreamChan = c +} + +// SetCloseTimeout sets the amount of time close will wait for +// streams to finish before terminating the underlying network +// connection. Setting the timeout to 0 will cause close to +// wait forever, which is the default. +func (s *Connection) SetCloseTimeout(timeout time.Duration) { + s.closeTimeout = timeout +} + +// SetIdleTimeout sets the amount of time the connection may sit idle before +// it is forcefully terminated. +func (s *Connection) SetIdleTimeout(timeout time.Duration) { + s.framer.setIdleTimeout(timeout) +} + +func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + headerFrame := &spdy.HeadersFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(headerFrame) +} + +func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + replyFrame := &spdy.SynReplyFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(replyFrame) +} + +func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error { + resetFrame := &spdy.RstStreamFrame{ + StreamId: streamId, + Status: status, + } + + return s.framer.WriteFrame(resetFrame) +} + +func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error { + return s.sendResetFrame(status, stream.streamId) +} + +func (s *Connection) sendStream(stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + stream.finished = true + } + + var parentId spdy.StreamId + if stream.parent != nil { + parentId = stream.parent.streamId + } + + streamFrame := &spdy.SynStreamFrame{ + StreamId: spdy.StreamId(stream.streamId), + AssociatedToStreamId: spdy.StreamId(parentId), + Headers: stream.headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(streamFrame) +} + +// getNextStreamId returns the next sequential id +// every call should produce a unique value or an error +func (s *Connection) getNextStreamId() spdy.StreamId { + sid := s.nextStreamId + if sid > 0x7fffffff { + return 0 + } + s.nextStreamId = s.nextStreamId + 2 + return sid +} + +// PeekNextStreamId returns the next sequential id and keeps the next id untouched +func (s *Connection) PeekNextStreamId() spdy.StreamId { + sid := s.nextStreamId + return sid +} + +func (s *Connection) validateStreamId(rid spdy.StreamId) error { + if rid > 0x7fffffff || rid < s.receivedStreamId { + return ErrInvalidStreamId + } + s.receivedStreamId = rid + 2 + return nil +} + +func (s *Connection) addStream(stream *Stream) { + s.streamCond.L.Lock() + s.streams[stream.streamId] = stream + debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) removeStream(stream *Stream) { + s.streamCond.L.Lock() + delete(s.streams, stream.streamId) + debugMessage("(%p) (%p) Stream removed, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) { + s.streamLock.RLock() + stream, ok = s.streams[streamId] + s.streamLock.RUnlock() + return +} + +// FindStream looks up the given stream id and either waits for the +// stream to be found or returns nil if the stream id is no longer +// valid. +func (s *Connection) FindStream(streamId uint32) *Stream { + var stream *Stream + var ok bool + s.streamCond.L.Lock() + stream, ok = s.streams[spdy.StreamId(streamId)] + debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok) + for !ok && streamId >= uint32(s.receivedStreamId) { + s.streamCond.Wait() + stream, ok = s.streams[spdy.StreamId(streamId)] + } + s.streamCond.L.Unlock() + return stream +} + +func (s *Connection) CloseChan() <-chan bool { + return s.closeChan +} diff --git a/vendor/github.com/docker/spdystream/handlers.go b/vendor/github.com/docker/spdystream/handlers.go new file mode 100644 index 0000000000..b59fa5fdcd --- /dev/null +++ b/vendor/github.com/docker/spdystream/handlers.go @@ -0,0 +1,38 @@ +package spdystream + +import ( + "io" + "net/http" +) + +// MirrorStreamHandler mirrors all streams. +func MirrorStreamHandler(stream *Stream) { + replyErr := stream.SendReply(http.Header{}, false) + if replyErr != nil { + return + } + + go func() { + io.Copy(stream, stream) + stream.Close() + }() + go func() { + for { + header, receiveErr := stream.ReceiveHeader() + if receiveErr != nil { + return + } + sendErr := stream.SendHeader(header, false) + if sendErr != nil { + return + } + } + }() +} + +// NoopStreamHandler does nothing when stream connects, most +// likely used with RejectAuthHandler which will not allow any +// streams to make it to the stream handler. +func NoOpStreamHandler(stream *Stream) { + stream.SendReply(http.Header{}, false) +} diff --git a/vendor/github.com/docker/spdystream/priority.go b/vendor/github.com/docker/spdystream/priority.go new file mode 100644 index 0000000000..fc8582b5c6 --- /dev/null +++ b/vendor/github.com/docker/spdystream/priority.go @@ -0,0 +1,98 @@ +package spdystream + +import ( + "container/heap" + "sync" + + "github.com/docker/spdystream/spdy" +) + +type prioritizedFrame struct { + frame spdy.Frame + priority uint8 + insertId uint64 +} + +type frameQueue []*prioritizedFrame + +func (fq frameQueue) Len() int { + return len(fq) +} + +func (fq frameQueue) Less(i, j int) bool { + if fq[i].priority == fq[j].priority { + return fq[i].insertId < fq[j].insertId + } + return fq[i].priority < fq[j].priority +} + +func (fq frameQueue) Swap(i, j int) { + fq[i], fq[j] = fq[j], fq[i] +} + +func (fq *frameQueue) Push(x interface{}) { + *fq = append(*fq, x.(*prioritizedFrame)) +} + +func (fq *frameQueue) Pop() interface{} { + old := *fq + n := len(old) + *fq = old[0 : n-1] + return old[n-1] +} + +type PriorityFrameQueue struct { + queue *frameQueue + c *sync.Cond + size int + nextInsertId uint64 + drain bool +} + +func NewPriorityFrameQueue(size int) *PriorityFrameQueue { + queue := make(frameQueue, 0, size) + heap.Init(&queue) + + return &PriorityFrameQueue{ + queue: &queue, + size: size, + c: sync.NewCond(&sync.Mutex{}), + } +} + +func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() >= q.size { + q.c.Wait() + } + pFrame := &prioritizedFrame{ + frame: frame, + priority: priority, + insertId: q.nextInsertId, + } + q.nextInsertId = q.nextInsertId + 1 + heap.Push(q.queue, pFrame) + q.c.Signal() +} + +func (q *PriorityFrameQueue) Pop() spdy.Frame { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() == 0 { + if q.drain { + return nil + } + q.c.Wait() + } + frame := heap.Pop(q.queue).(*prioritizedFrame).frame + q.c.Signal() + return frame +} + +func (q *PriorityFrameQueue) Drain() { + q.c.L.Lock() + defer q.c.L.Unlock() + q.drain = true + q.c.Broadcast() +} diff --git a/vendor/github.com/docker/spdystream/priority_test.go b/vendor/github.com/docker/spdystream/priority_test.go new file mode 100644 index 0000000000..0746be2a80 --- /dev/null +++ b/vendor/github.com/docker/spdystream/priority_test.go @@ -0,0 +1,108 @@ +package spdystream + +import ( + "sync" + "testing" + "time" + + "github.com/docker/spdystream/spdy" +) + +func TestPriorityQueueOrdering(t *testing.T) { + queue := NewPriorityFrameQueue(150) + data1 := &spdy.DataFrame{} + data2 := &spdy.DataFrame{} + data3 := &spdy.DataFrame{} + data4 := &spdy.DataFrame{} + queue.Push(data1, 2) + queue.Push(data2, 1) + queue.Push(data3, 1) + queue.Push(data4, 0) + + if queue.Pop() != data4 { + t.Fatalf("Wrong order, expected data4 first") + } + if queue.Pop() != data2 { + t.Fatalf("Wrong order, expected data2 second") + } + if queue.Pop() != data3 { + t.Fatalf("Wrong order, expected data3 third") + } + if queue.Pop() != data1 { + t.Fatalf("Wrong order, expected data1 fourth") + } + + // Insert 50 Medium priority frames + for i := spdy.StreamId(50); i < 100; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, 1) + } + // Insert 50 low priority frames + for i := spdy.StreamId(100); i < 150; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, 2) + } + // Insert 50 high priority frames + for i := spdy.StreamId(0); i < 50; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, 0) + } + + for i := spdy.StreamId(0); i < 150; i++ { + frame := queue.Pop() + if frame.(*spdy.DataFrame).StreamId != i { + t.Fatalf("Wrong frame\nActual: %d\nExpecting: %d", frame.(*spdy.DataFrame).StreamId, i) + } + } +} + +func TestPriorityQueueSync(t *testing.T) { + queue := NewPriorityFrameQueue(150) + var wg sync.WaitGroup + insertRange := func(start, stop spdy.StreamId, priority uint8) { + for i := start; i < stop; i++ { + queue.Push(&spdy.DataFrame{StreamId: i}, priority) + } + wg.Done() + } + wg.Add(3) + go insertRange(spdy.StreamId(100), spdy.StreamId(150), 2) + go insertRange(spdy.StreamId(0), spdy.StreamId(50), 0) + go insertRange(spdy.StreamId(50), spdy.StreamId(100), 1) + + wg.Wait() + for i := spdy.StreamId(0); i < 150; i++ { + frame := queue.Pop() + if frame.(*spdy.DataFrame).StreamId != i { + t.Fatalf("Wrong frame\nActual: %d\nExpecting: %d", frame.(*spdy.DataFrame).StreamId, i) + } + } +} + +func TestPriorityQueueBlocking(t *testing.T) { + queue := NewPriorityFrameQueue(15) + for i := 0; i < 15; i++ { + queue.Push(&spdy.DataFrame{}, 2) + } + doneChan := make(chan bool) + go func() { + queue.Push(&spdy.DataFrame{}, 2) + close(doneChan) + }() + select { + case <-doneChan: + t.Fatalf("Push succeeded, expected to block") + case <-time.After(time.Millisecond): + break + } + + queue.Pop() + + select { + case <-doneChan: + break + case <-time.After(time.Millisecond): + t.Fatalf("Push should have succeeded, but timeout reached") + } + + for i := 0; i < 15; i++ { + queue.Pop() + } +} diff --git a/vendor/github.com/docker/spdystream/spdy/dictionary.go b/vendor/github.com/docker/spdystream/spdy/dictionary.go new file mode 100644 index 0000000000..5a5ff0e14c --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/dictionary.go @@ -0,0 +1,187 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +// headerDictionary is the dictionary sent to the zlib compressor/decompressor. +var headerDictionary = []byte{ + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, +} diff --git a/vendor/github.com/docker/spdystream/spdy/read.go b/vendor/github.com/docker/spdystream/spdy/read.go new file mode 100644 index 0000000000..9359a95015 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/read.go @@ -0,0 +1,348 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "compress/zlib" + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynStreamFrame(h, frame) +} + +func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynReplyFrame(h, frame) +} + +func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + var numSettings uint32 + if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { + return err + } + frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) + for i := uint32(0); i < numSettings; i++ { + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { + return err + } + frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) + frame.FlagIdValues[i].Id &= 0xffffff + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { + return err + } + } + return nil +} + +func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { + return err + } + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, StreamId(frame.Id)} + } + return nil +} + +func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + return nil +} + +func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readHeadersFrame(h, frame) +} + +func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { + return err + } + return nil +} + +func newControlFrame(frameType ControlFrameType) (controlFrame, error) { + ctor, ok := cframeCtor[frameType] + if !ok { + return nil, &Error{Err: InvalidControlFrame} + } + return ctor(), nil +} + +var cframeCtor = map[ControlFrameType]func() controlFrame{ + TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, + TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, + TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, + TypeSettings: func() controlFrame { return new(SettingsFrame) }, + TypePing: func() controlFrame { return new(PingFrame) }, + TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, + TypeHeaders: func() controlFrame { return new(HeadersFrame) }, + TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, +} + +func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { + if f.headerDecompressor != nil { + f.headerReader.N = payloadSize + return nil + } + f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} + decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) + if err != nil { + return err + } + f.headerDecompressor = decompressor + return nil +} + +// ReadFrame reads SPDY encoded data and returns a decompressed Frame. +func (f *Framer) ReadFrame() (Frame, error) { + var firstWord uint32 + if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { + return nil, err + } + if firstWord&0x80000000 != 0 { + frameType := ControlFrameType(firstWord & 0xffff) + version := uint16(firstWord >> 16 & 0x7fff) + return f.parseControlFrame(version, frameType) + } + return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) +} + +func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + flags := ControlFlags((length & 0xff000000) >> 24) + length &= 0xffffff + header := ControlFrameHeader{version, frameType, flags, length} + cframe, err := newControlFrame(frameType) + if err != nil { + return nil, err + } + if err = cframe.read(header, f); err != nil { + return nil, err + } + return cframe, nil +} + +func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { + var numHeaders uint32 + if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { + return nil, err + } + var e error + h := make(http.Header, int(numHeaders)) + for i := 0; i < int(numHeaders); i++ { + var length uint32 + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + nameBytes := make([]byte, length) + if _, err := io.ReadFull(r, nameBytes); err != nil { + return nil, err + } + name := string(nameBytes) + if name != strings.ToLower(name) { + e = &Error{UnlowercasedHeaderName, streamId} + name = strings.ToLower(name) + } + if h[name] != nil { + e = &Error{DuplicateHeaders, streamId} + } + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + value := make([]byte, length) + if _, err := io.ReadFull(r, value); err != nil { + return nil, err + } + valueList := strings.Split(string(value), headerValueSeparator) + for _, v := range valueList { + h.Add(name, v) + } + } + if e != nil { + return h, e + } + return h, nil +} + +func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { + return err + } + frame.Priority >>= 5 + if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 10)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidReqHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidRespHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + var invalidHeaders map[string]bool + if frame.StreamId%2 == 0 { + invalidHeaders = invalidReqHeaders + } else { + invalidHeaders = invalidRespHeaders + } + for h := range frame.Headers { + if invalidHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + var frame DataFrame + frame.StreamId = streamId + frame.Flags = DataFlags(length >> 24) + length &= 0xffffff + frame.Data = make([]byte, length) + if _, err := io.ReadFull(f.r, frame.Data); err != nil { + return nil, err + } + if frame.StreamId == 0 { + return nil, &Error{ZeroStreamId, 0} + } + return &frame, nil +} diff --git a/vendor/github.com/docker/spdystream/spdy/spdy_test.go b/vendor/github.com/docker/spdystream/spdy/spdy_test.go new file mode 100644 index 0000000000..ce581f1d05 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/spdy_test.go @@ -0,0 +1,644 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "bytes" + "compress/zlib" + "encoding/base64" + "io" + "io/ioutil" + "net/http" + "reflect" + "testing" +) + +var HeadersFixture = http.Header{ + "Url": []string{"http://www.google.com/"}, + "Method": []string{"get"}, + "Version": []string{"http/1.1"}, +} + +func TestHeaderParsing(t *testing.T) { + var headerValueBlockBuf bytes.Buffer + writeHeaderValueBlock(&headerValueBlockBuf, HeadersFixture) + const bogusStreamId = 1 + newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) + if err != nil { + t.Fatal("parseHeaderValueBlock:", err) + } + if !reflect.DeepEqual(HeadersFixture, newHeaders) { + t.Fatal("got: ", newHeaders, "\nwant: ", HeadersFixture) + } +} + +func TestCreateParseSynStreamFrameCompressionDisable(t *testing.T) { + buffer := new(bytes.Buffer) + // Fixture framer for no compression test. + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestCreateParseSynStreamFrameCompressionEnable(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestCreateParseSynReplyFrameCompressionDisable(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + synReplyFrame := SynReplyFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynReply, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err := framer.WriteFrame(&synReplyFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedSynReplyFrame, ok := frame.(*SynReplyFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { + t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) + } +} + +func TestCreateParseSynReplyFrameCompressionEnable(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + synReplyFrame := SynReplyFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynReply, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + if err := framer.WriteFrame(&synReplyFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedSynReplyFrame, ok := frame.(*SynReplyFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { + t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) + } +} + +func TestCreateParseRstStream(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + rstStreamFrame := RstStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeRstStream, + }, + StreamId: 1, + Status: InvalidStream, + } + if err := framer.WriteFrame(&rstStreamFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedRstStreamFrame, ok := frame.(*RstStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) { + t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame) + } +} + +func TestCreateParseSettings(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + settingsFrame := SettingsFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSettings, + }, + FlagIdValues: []SettingsFlagIdValue{ + {FlagSettingsPersistValue, SettingsCurrentCwnd, 10}, + {FlagSettingsPersisted, SettingsUploadBandwidth, 1}, + }, + } + if err := framer.WriteFrame(&settingsFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedSettingsFrame, ok := frame.(*SettingsFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) { + t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame) + } +} + +func TestCreateParsePing(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + pingFrame := PingFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypePing, + }, + Id: 31337, + } + if err := framer.WriteFrame(&pingFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + if pingFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", pingFrame) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedPingFrame, ok := frame.(*PingFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if parsedPingFrame.CFHeader.Flags != 0 { + t.Fatal("Parsed incorrect frame type:", parsedPingFrame) + } + if !reflect.DeepEqual(pingFrame, *parsedPingFrame) { + t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame) + } +} + +func TestCreateParseGoAway(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + goAwayFrame := GoAwayFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeGoAway, + }, + LastGoodStreamId: 31337, + Status: 1, + } + if err := framer.WriteFrame(&goAwayFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + if goAwayFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", goAwayFrame) + } + if goAwayFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", goAwayFrame) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedGoAwayFrame, ok := frame.(*GoAwayFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if parsedGoAwayFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", parsedGoAwayFrame) + } + if parsedGoAwayFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", parsedGoAwayFrame) + } + if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) { + t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame) + } +} + +func TestCreateParseHeadersFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer := &Framer{ + headerCompressionDisabled: true, + w: buffer, + headerBuf: new(bytes.Buffer), + r: buffer, + } + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + } + headersFrame.Headers = HeadersFixture + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame without compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame without compression:", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } +} + +func TestCreateParseHeadersFrameCompressionEnable(t *testing.T) { + buffer := new(bytes.Buffer) + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + } + headersFrame.Headers = HeadersFixture + + framer, err := NewFramer(buffer, buffer) + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame with compression:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame with compression:", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } +} + +func TestCreateParseWindowUpdateFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + windowUpdateFrame := WindowUpdateFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeWindowUpdate, + }, + StreamId: 31337, + DeltaWindowSize: 1, + } + if err := framer.WriteFrame(&windowUpdateFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + if windowUpdateFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", windowUpdateFrame) + } + if windowUpdateFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", windowUpdateFrame) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedWindowUpdateFrame, ok := frame.(*WindowUpdateFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if parsedWindowUpdateFrame.CFHeader.Flags != 0 { + t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame) + } + if parsedWindowUpdateFrame.CFHeader.length != 8 { + t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame) + } + if !reflect.DeepEqual(windowUpdateFrame, *parsedWindowUpdateFrame) { + t.Fatal("got: ", *parsedWindowUpdateFrame, "\nwant: ", windowUpdateFrame) + } +} + +func TestCreateParseDataFrame(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + dataFrame := DataFrame{ + StreamId: 1, + Data: []byte{'h', 'e', 'l', 'l', 'o'}, + } + if err := framer.WriteFrame(&dataFrame); err != nil { + t.Fatal("WriteFrame:", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame:", err) + } + parsedDataFrame, ok := frame.(*DataFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(dataFrame, *parsedDataFrame) { + t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame) + } +} + +func TestCompressionContextAcrossFrames(t *testing.T) { + buffer := new(bytes.Buffer) + framer, err := NewFramer(buffer, buffer) + if err != nil { + t.Fatal("Failed to create new framer:", err) + } + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + Headers: HeadersFixture, + } + if err := framer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame (HEADERS):", err) + } + synStreamFrame := SynStreamFrame{ + ControlFrameHeader{ + Version, + TypeSynStream, + 0, // Flags + 0, // length + }, + 2, // StreamId + 0, // AssociatedTOStreamID + 0, // Priority + 1, // Slot + nil, // Headers + } + synStreamFrame.Headers = HeadersFixture + + if err := framer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame (SYN_STREAM):", err) + } + frame, err := framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes()) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatalf("expected HeadersFrame; got %T %v", frame, frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + frame, err = framer.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes()) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame) + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestMultipleSPDYFrames(t *testing.T) { + // Initialize the framers. + pr1, pw1 := io.Pipe() + pr2, pw2 := io.Pipe() + writer, err := NewFramer(pw1, pr2) + if err != nil { + t.Fatal("Failed to create writer:", err) + } + reader, err := NewFramer(pw2, pr1) + if err != nil { + t.Fatal("Failed to create reader:", err) + } + + // Set up the frames we're actually transferring. + headersFrame := HeadersFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeHeaders, + }, + StreamId: 2, + Headers: HeadersFixture, + } + synStreamFrame := SynStreamFrame{ + CFHeader: ControlFrameHeader{ + version: Version, + frameType: TypeSynStream, + }, + StreamId: 2, + Headers: HeadersFixture, + } + + // Start the goroutines to write the frames. + go func() { + if err := writer.WriteFrame(&headersFrame); err != nil { + t.Fatal("WriteFrame (HEADERS): ", err) + } + if err := writer.WriteFrame(&synStreamFrame); err != nil { + t.Fatal("WriteFrame (SYN_STREAM): ", err) + } + }() + + // Read the frames and verify they look as expected. + frame, err := reader.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (HEADERS): ", err) + } + parsedHeadersFrame, ok := frame.(*HeadersFrame) + if !ok { + t.Fatal("Parsed incorrect frame type:", frame) + } + if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { + t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) + } + frame, err = reader.ReadFrame() + if err != nil { + t.Fatal("ReadFrame (SYN_STREAM):", err) + } + parsedSynStreamFrame, ok := frame.(*SynStreamFrame) + if !ok { + t.Fatal("Parsed incorrect frame type.") + } + if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { + t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) + } +} + +func TestReadMalformedZlibHeader(t *testing.T) { + // These were constructed by corrupting the first byte of the zlib + // header after writing. + malformedStructs := map[string]string{ + "SynStreamFrame": "gAIAAQAAABgAAAACAAAAAAAAF/nfolGyYmAAAAAA//8=", + "SynReplyFrame": "gAIAAgAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==", + "HeadersFrame": "gAIACAAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==", + } + for name, bad := range malformedStructs { + b, err := base64.StdEncoding.DecodeString(bad) + if err != nil { + t.Errorf("Unable to decode base64 encoded frame %s: %v", name, err) + } + buf := bytes.NewBuffer(b) + reader, err := NewFramer(buf, buf) + if err != nil { + t.Fatalf("NewFramer: %v", err) + } + _, err = reader.ReadFrame() + if err != zlib.ErrHeader { + t.Errorf("Frame %s, expected: %#v, actual: %#v", name, zlib.ErrHeader, err) + } + } +} + +// TODO: these tests are too weak for updating SPDY spec. Fix me. + +type zeroStream struct { + frame Frame + encoded string +} + +var streamIdZeroFrames = map[string]zeroStream{ + "SynStreamFrame": { + &SynStreamFrame{StreamId: 0}, + "gAIAAQAAABgAAAAAAAAAAAAAePnfolGyYmAAAAAA//8=", + }, + "SynReplyFrame": { + &SynReplyFrame{StreamId: 0}, + "gAIAAgAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==", + }, + "RstStreamFrame": { + &RstStreamFrame{StreamId: 0}, + "gAIAAwAAAAgAAAAAAAAAAA==", + }, + "HeadersFrame": { + &HeadersFrame{StreamId: 0}, + "gAIACAAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==", + }, + "DataFrame": { + &DataFrame{StreamId: 0}, + "AAAAAAAAAAA=", + }, + "PingFrame": { + &PingFrame{Id: 0}, + "gAIABgAAAAQAAAAA", + }, +} + +func TestNoZeroStreamId(t *testing.T) { + t.Log("skipping") // TODO: update to work with SPDY3 + return + + for name, f := range streamIdZeroFrames { + b, err := base64.StdEncoding.DecodeString(f.encoded) + if err != nil { + t.Errorf("Unable to decode base64 encoded frame %s: %v", f, err) + continue + } + framer, err := NewFramer(ioutil.Discard, bytes.NewReader(b)) + if err != nil { + t.Fatalf("NewFramer: %v", err) + } + err = framer.WriteFrame(f.frame) + checkZeroStreamId(t, name, "WriteFrame", err) + + _, err = framer.ReadFrame() + checkZeroStreamId(t, name, "ReadFrame", err) + } +} + +func checkZeroStreamId(t *testing.T, frame string, method string, err error) { + if err == nil { + t.Errorf("%s ZeroStreamId, no error on %s", method, frame) + return + } + eerr, ok := err.(*Error) + if !ok || eerr.Err != ZeroStreamId { + t.Errorf("%s ZeroStreamId, incorrect error %#v, frame %s", method, eerr, frame) + } +} diff --git a/vendor/github.com/docker/spdystream/spdy/types.go b/vendor/github.com/docker/spdystream/spdy/types.go new file mode 100644 index 0000000000..7b6ee9c6f2 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/types.go @@ -0,0 +1,275 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package spdy implements the SPDY protocol (currently SPDY/3), described in +// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. +package spdy + +import ( + "bytes" + "compress/zlib" + "io" + "net/http" +) + +// Version is the protocol version number that this package implements. +const Version = 3 + +// ControlFrameType stores the type field in a control frame header. +type ControlFrameType uint16 + +const ( + TypeSynStream ControlFrameType = 0x0001 + TypeSynReply = 0x0002 + TypeRstStream = 0x0003 + TypeSettings = 0x0004 + TypePing = 0x0006 + TypeGoAway = 0x0007 + TypeHeaders = 0x0008 + TypeWindowUpdate = 0x0009 +) + +// ControlFlags are the flags that can be set on a control frame. +type ControlFlags uint8 + +const ( + ControlFlagFin ControlFlags = 0x01 + ControlFlagUnidirectional = 0x02 + ControlFlagSettingsClearSettings = 0x01 +) + +// DataFlags are the flags that can be set on a data frame. +type DataFlags uint8 + +const ( + DataFlagFin DataFlags = 0x01 +) + +// MaxDataLength is the maximum number of bytes that can be stored in one frame. +const MaxDataLength = 1<<24 - 1 + +// headerValueSepator separates multiple header values. +const headerValueSeparator = "\x00" + +// Frame is a single SPDY frame in its unpacked in-memory representation. Use +// Framer to read and write it. +type Frame interface { + write(f *Framer) error +} + +// ControlFrameHeader contains all the fields in a control frame header, +// in its unpacked in-memory representation. +type ControlFrameHeader struct { + // Note, high bit is the "Control" bit. + version uint16 // spdy version number + frameType ControlFrameType + Flags ControlFlags + length uint32 // length of data field +} + +type controlFrame interface { + Frame + read(h ControlFrameHeader, f *Framer) error +} + +// StreamId represents a 31-bit value identifying the stream. +type StreamId uint32 + +// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM +// frame. +type SynStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to + Priority uint8 // priority of this frame (3-bit) + Slot uint8 // index in the server's credential vector of the client certificate + Headers http.Header +} + +// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. +type SynReplyFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// RstStreamStatus represents the status that led to a RST_STREAM. +type RstStreamStatus uint32 + +const ( + ProtocolError RstStreamStatus = iota + 1 + InvalidStream + RefusedStream + UnsupportedVersion + Cancel + InternalError + FlowControlError + StreamInUse + StreamAlreadyClosed + InvalidCredentials + FrameTooLarge +) + +// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM +// frame. +type RstStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Status RstStreamStatus +} + +// SettingsFlag represents a flag in a SETTINGS frame. +type SettingsFlag uint8 + +const ( + FlagSettingsPersistValue SettingsFlag = 0x1 + FlagSettingsPersisted = 0x2 +) + +// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. +type SettingsId uint32 + +const ( + SettingsUploadBandwidth SettingsId = iota + 1 + SettingsDownloadBandwidth + SettingsRoundTripTime + SettingsMaxConcurrentStreams + SettingsCurrentCwnd + SettingsDownloadRetransRate + SettingsInitialWindowSize + SettingsClientCretificateVectorSize +) + +// SettingsFlagIdValue is the unpacked, in-memory representation of the +// combined flag/id/value for a setting in a SETTINGS frame. +type SettingsFlagIdValue struct { + Flag SettingsFlag + Id SettingsId + Value uint32 +} + +// SettingsFrame is the unpacked, in-memory representation of a SPDY +// SETTINGS frame. +type SettingsFrame struct { + CFHeader ControlFrameHeader + FlagIdValues []SettingsFlagIdValue +} + +// PingFrame is the unpacked, in-memory representation of a PING frame. +type PingFrame struct { + CFHeader ControlFrameHeader + Id uint32 // unique id for this ping, from server is even, from client is odd. +} + +// GoAwayStatus represents the status in a GoAwayFrame. +type GoAwayStatus uint32 + +const ( + GoAwayOK GoAwayStatus = iota + GoAwayProtocolError + GoAwayInternalError +) + +// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. +type GoAwayFrame struct { + CFHeader ControlFrameHeader + LastGoodStreamId StreamId // last stream id which was accepted by sender + Status GoAwayStatus +} + +// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. +type HeadersFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// WindowUpdateFrame is the unpacked, in-memory representation of a +// WINDOW_UPDATE frame. +type WindowUpdateFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + DeltaWindowSize uint32 // additional number of bytes to existing window size +} + +// TODO: Implement credential frame and related methods. + +// DataFrame is the unpacked, in-memory representation of a DATA frame. +type DataFrame struct { + // Note, high bit is the "Control" bit. Should be 0 for data frames. + StreamId StreamId + Flags DataFlags + Data []byte // payload data of this frame +} + +// A SPDY specific error. +type ErrorCode string + +const ( + UnlowercasedHeaderName ErrorCode = "header was not lowercased" + DuplicateHeaders = "multiple headers with same name" + WrongCompressedPayloadSize = "compressed payload size was incorrect" + UnknownFrameType = "unknown frame type" + InvalidControlFrame = "invalid control frame" + InvalidDataFrame = "invalid data frame" + InvalidHeaderPresent = "frame contained invalid header" + ZeroStreamId = "stream id zero is disallowed" +) + +// Error contains both the type of error and additional values. StreamId is 0 +// if Error is not associated with a stream. +type Error struct { + Err ErrorCode + StreamId StreamId +} + +func (e *Error) Error() string { + return string(e.Err) +} + +var invalidReqHeaders = map[string]bool{ + "Connection": true, + "Host": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +var invalidRespHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +// Framer handles serializing/deserializing SPDY frames, including compressing/ +// decompressing payloads. +type Framer struct { + headerCompressionDisabled bool + w io.Writer + headerBuf *bytes.Buffer + headerCompressor *zlib.Writer + r io.Reader + headerReader io.LimitedReader + headerDecompressor io.ReadCloser +} + +// NewFramer allocates a new Framer for a given SPDY connection, represented by +// a io.Writer and io.Reader. Note that Framer will read and write individual fields +// from/to the Reader and Writer, so the caller should pass in an appropriately +// buffered implementation to optimize performance. +func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { + compressBuf := new(bytes.Buffer) + compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) + if err != nil { + return nil, err + } + framer := &Framer{ + w: w, + headerBuf: compressBuf, + headerCompressor: compressor, + r: r, + } + return framer, nil +} diff --git a/vendor/github.com/docker/spdystream/spdy/write.go b/vendor/github.com/docker/spdystream/spdy/write.go new file mode 100644 index 0000000000..b212f66a23 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/write.go @@ -0,0 +1,318 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) write(f *Framer) error { + return f.writeSynStreamFrame(frame) +} + +func (frame *SynReplyFrame) write(f *Framer) error { + return f.writeSynReplyFrame(frame) +} + +func (frame *RstStreamFrame) write(f *Framer) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeRstStream + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return +} + +func (frame *SettingsFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSettings + frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { + return + } + for _, flagIdValue := range frame.FlagIdValues { + flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) + if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { + return + } + } + return +} + +func (frame *PingFrame) write(f *Framer) (err error) { + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypePing + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 4 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { + return + } + return +} + +func (frame *GoAwayFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeGoAway + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return nil +} + +func (frame *HeadersFrame) write(f *Framer) error { + return f.writeHeadersFrame(frame) +} + +func (frame *WindowUpdateFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeWindowUpdate + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { + return + } + return nil +} + +func (frame *DataFrame) write(f *Framer) error { + return f.writeDataFrame(frame) +} + +// WriteFrame writes a frame. +func (f *Framer) WriteFrame(frame Frame) error { + return frame.write(f) +} + +func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { + if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { + return err + } + flagsAndLength := uint32(h.Flags)<<24 | h.length + if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { + return err + } + return nil +} + +func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { + n = 0 + if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { + return + } + n += 2 + for name, values := range h { + if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { + return + } + n += 2 + name = strings.ToLower(name) + if _, err = io.WriteString(w, name); err != nil { + return + } + n += len(name) + v := strings.Join(values, headerValueSeparator) + if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { + return + } + n += 2 + if _, err = io.WriteString(w, v); err != nil { + return + } + n += len(v) + } + return +} + +func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynStream + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { + return err + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return err + } + f.headerBuf.Reset() + return nil +} + +func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynReply + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeHeaders + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { + return &Error{InvalidDataFrame, frame.StreamId} + } + + // Serialize frame to Writer. + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) + if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { + return + } + if _, err = f.w.Write(frame.Data); err != nil { + return + } + return nil +} diff --git a/vendor/github.com/docker/spdystream/spdy_bench_test.go b/vendor/github.com/docker/spdystream/spdy_bench_test.go new file mode 100644 index 0000000000..6f9e491015 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy_bench_test.go @@ -0,0 +1,113 @@ +package spdystream + +import ( + "fmt" + "io" + "net" + "net/http" + "sync" + "testing" +) + +func configureServer() (io.Closer, string, *sync.WaitGroup) { + authenticated = true + wg := &sync.WaitGroup{} + server, listen, serverErr := runServer(wg) + + if serverErr != nil { + panic(serverErr) + } + + return server, listen, wg +} + +func BenchmarkDial10000(b *testing.B) { + server, addr, wg := configureServer() + + defer func() { + server.Close() + wg.Wait() + }() + + for i := 0; i < b.N; i++ { + conn, dialErr := net.Dial("tcp", addr) + if dialErr != nil { + panic(fmt.Sprintf("Error dialing server: %s", dialErr)) + } + conn.Close() + } +} + +func BenchmarkDialWithSPDYStream10000(b *testing.B) { + server, addr, wg := configureServer() + + defer func() { + server.Close() + wg.Wait() + }() + + for i := 0; i < b.N; i++ { + conn, dialErr := net.Dial("tcp", addr) + if dialErr != nil { + b.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + b.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + closeErr := spdyConn.Close() + if closeErr != nil { + b.Fatalf("Error closing connection: %s, closeErr") + } + } +} + +func benchmarkStreamWithDataAndSize(size uint64, b *testing.B) { + server, addr, wg := configureServer() + + defer func() { + server.Close() + wg.Wait() + }() + + for i := 0; i < b.N; i++ { + conn, dialErr := net.Dial("tcp", addr) + if dialErr != nil { + b.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + b.Fatalf("Error creating spdy connection: %s", spdyErr) + } + + go spdyConn.Serve(MirrorStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + + writer := make([]byte, size) + + stream.Write(writer) + + if err != nil { + panic(err) + } + + reader := make([]byte, size) + stream.Read(reader) + + stream.Close() + + closeErr := spdyConn.Close() + if closeErr != nil { + b.Fatalf("Error closing connection: %s, closeErr") + } + } +} + +func BenchmarkStreamWith1Byte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1, b) } +func BenchmarkStreamWith1KiloByte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1024, b) } +func BenchmarkStreamWith1Megabyte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1024*1024, b) } diff --git a/vendor/github.com/docker/spdystream/spdy_test.go b/vendor/github.com/docker/spdystream/spdy_test.go new file mode 100644 index 0000000000..7f0e3a4487 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy_test.go @@ -0,0 +1,1171 @@ +package spdystream + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" + + "github.com/docker/spdystream/spdy" +) + +func TestSpdyStreams(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + message := []byte("hello") + writeErr := stream.WriteData(message, false) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + buf := make([]byte, 10) + n, readErr := stream.Read(buf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 5 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n) + } + if bytes.Compare(buf[:n], message) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message) + } + + headers := http.Header{ + "TestKey": []string{"TestVal"}, + } + sendErr := stream.SendHeader(headers, false) + if sendErr != nil { + t.Fatalf("Error sending headers: %s", sendErr) + } + receiveHeaders, receiveErr := stream.ReceiveHeader() + if receiveErr != nil { + t.Fatalf("Error receiving headers: %s", receiveErr) + } + if len(receiveHeaders) != 1 { + t.Fatalf("Unexpected number of headers:\nActual: %d\nExpecting:%d", len(receiveHeaders), 1) + } + testVal := receiveHeaders.Get("TestKey") + if testVal != "TestVal" { + t.Fatalf("Wrong test value:\nActual: %q\nExpecting: %q", testVal, "TestVal") + } + + writeErr = stream.WriteData(message, true) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + smallBuf := make([]byte, 3) + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 3 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n) + } + if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message) + } + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 2 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n) + } + if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n]) + } + + n, readErr = stream.Read(buf) + if readErr != io.EOF { + t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n) + } + + // Closing again should return error since stream is already closed + streamCloseErr := stream.Close() + if streamCloseErr == nil { + t.Fatalf("No error closing finished stream") + } + if streamCloseErr != ErrWriteClosedStream { + t.Fatalf("Unexpected error closing stream: %s", streamCloseErr) + } + + streamResetErr := stream.Reset() + if streamResetErr != nil { + t.Fatalf("Error reseting stream: %s", streamResetErr) + } + + authenticated = false + badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if badStreamErr != nil { + t.Fatalf("Error creating stream: %s", badStreamErr) + } + + waitErr = badStream.Wait() + if waitErr == nil { + t.Fatalf("Did not receive error creating stream") + } + if waitErr != ErrReset { + t.Fatalf("Unexpected error creating stream: %s", waitErr) + } + streamCloseErr = badStream.Close() + if streamCloseErr == nil { + t.Fatalf("No error closing bad stream") + } + + spdyCloseErr := spdyConn.Close() + if spdyCloseErr != nil { + t.Fatalf("Error closing spdy connection: %s", spdyCloseErr) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestPing(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + pingTime, pingErr := spdyConn.Ping() + if pingErr != nil { + t.Fatalf("Error pinging server: %s", pingErr) + } + if pingTime == time.Duration(0) { + t.Fatalf("Expecting non-zero ping time") + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestHalfClose(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + message := []byte("hello and will read after close") + writeErr := stream.WriteData(message, false) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + streamCloseErr := stream.Close() + if streamCloseErr != nil { + t.Fatalf("Error closing stream: %s", streamCloseErr) + } + + buf := make([]byte, 40) + n, readErr := stream.Read(buf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 31 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n) + } + if bytes.Compare(buf[:n], message) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message) + } + + spdyCloseErr := spdyConn.Close() + if spdyCloseErr != nil { + t.Fatalf("Error closing spdy connection: %s", spdyCloseErr) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestUnexpectedRemoteConnectionClosed(t *testing.T) { + tt := []struct { + closeReceiver bool + closeSender bool + }{ + {closeReceiver: true, closeSender: false}, + {closeReceiver: false, closeSender: true}, + {closeReceiver: false, closeSender: false}, + } + for tix, tc := range tt { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + t.Fatalf("Error listening: %v", listenErr) + } + + var serverConn net.Conn + var connErr error + go func() { + serverConn, connErr = listener.Accept() + if connErr != nil { + t.Fatalf("Error accepting: %v", connErr) + } + + serverSpdyConn, _ := NewConnection(serverConn, true) + go serverSpdyConn.Serve(func(stream *Stream) { + stream.SendReply(http.Header{}, tc.closeSender) + }) + }() + + conn, dialErr := net.Dial("tcp", listener.Addr().String()) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + if tc.closeReceiver { + // make stream half closed, receive only + stream.Close() + } + + streamch := make(chan error, 1) + go func() { + b := make([]byte, 1) + _, err := stream.Read(b) + streamch <- err + }() + + closeErr := serverConn.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + + select { + case e := <-streamch: + if e == nil || e != io.EOF { + t.Fatalf("(%d) Expected to get an EOF stream error", tix) + } + } + + closeErr = conn.Close() + if closeErr != nil { + t.Fatalf("Error closing client connection: %s", closeErr) + } + + listenErr = listener.Close() + if listenErr != nil { + t.Fatalf("Error closing listener: %s", listenErr) + } + } +} + +func TestCloseNotification(t *testing.T) { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + t.Fatalf("Error listening: %v", listenErr) + } + listen := listener.Addr().String() + + serverConnChan := make(chan net.Conn) + go func() { + serverConn, err := listener.Accept() + if err != nil { + t.Fatalf("Error accepting: %v", err) + } + + serverSpdyConn, err := NewConnection(serverConn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + <-serverSpdyConn.CloseChan() + serverConnChan <- serverConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + // close client conn + err := conn.Close() + if err != nil { + t.Fatalf("Error closing client connection: %v", err) + } + + var serverConn net.Conn + select { + case serverConn = <-serverConnChan: + } + + err = serverConn.Close() + if err != nil { + t.Fatalf("Error closing serverConn: %v", err) + } + + listenErr = listener.Close() + if listenErr != nil { + t.Fatalf("Error closing listener: %s", listenErr) + } +} + +func TestIdleShutdownRace(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + + spdyConn.SetIdleTimeout(5 * time.Millisecond) + go func() { + time.Sleep(5 * time.Millisecond) + stream.Reset() + }() + + select { + case <-spdyConn.CloseChan(): + case <-time.After(20 * time.Millisecond): + t.Fatal("Timed out waiting for idle connection closure") + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleNoTimeoutSet(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + select { + case <-spdyConn.CloseChan(): + t.Fatal("Unexpected connection closure") + case <-time.After(10 * time.Millisecond): + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleClearTimeout(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(10 * time.Millisecond) + spdyConn.SetIdleTimeout(0) + select { + case <-spdyConn.CloseChan(): + t.Fatal("Unexpected connection closure") + case <-time.After(20 * time.Millisecond): + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleNoData(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(10 * time.Millisecond) + <-spdyConn.CloseChan() + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleWithData(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(25 * time.Millisecond) + + authenticated = true + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + + writeCh := make(chan struct{}) + + go func() { + b := []byte{1, 2, 3, 4, 5} + for i := 0; i < 10; i++ { + _, err = stream.Write(b) + if err != nil { + t.Fatalf("Error writing to stream: %v", err) + } + time.Sleep(10 * time.Millisecond) + } + close(writeCh) + }() + + writesFinished := false + +Loop: + for { + select { + case <-writeCh: + writesFinished = true + case <-spdyConn.CloseChan(): + if !writesFinished { + t.Fatal("Connection closed before all writes finished") + } + break Loop + } + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestIdleRace(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + spdyConn.SetIdleTimeout(10 * time.Millisecond) + + authenticated = true + + for i := 0; i < 10; i++ { + _, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + } + + <-spdyConn.CloseChan() + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestHalfClosedIdleTimeout(t *testing.T) { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + t.Fatalf("Error listening: %v", listenErr) + } + listen := listener.Addr().String() + + go func() { + serverConn, err := listener.Accept() + if err != nil { + t.Fatalf("Error accepting: %v", err) + } + + serverSpdyConn, err := NewConnection(serverConn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(func(s *Stream) { + s.SendReply(http.Header{}, true) + }) + serverSpdyConn.SetIdleTimeout(10 * time.Millisecond) + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + + time.Sleep(20 * time.Millisecond) + + stream.Reset() + + err = spdyConn.Close() + if err != nil { + t.Fatalf("Error closing client spdy conn: %v", err) + } +} + +func TestStreamReset(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + buf := []byte("dskjahfkdusahfkdsahfkdsafdkas") + for i := 0; i < 10; i++ { + if _, err := stream.Write(buf); err != nil { + t.Fatalf("Error writing to stream: %s", err) + } + } + for i := 0; i < 10; i++ { + if _, err := stream.Read(buf); err != nil { + t.Fatalf("Error reading from stream: %s", err) + } + } + + // fmt.Printf("Resetting...\n") + if err := stream.Reset(); err != nil { + t.Fatalf("Error reseting stream: %s", err) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +func TestStreamResetWithDataRemaining(t *testing.T) { + var wg sync.WaitGroup + server, listen, serverErr := runServer(&wg) + if serverErr != nil { + t.Fatalf("Error initializing server: %s", serverErr) + } + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + authenticated = true + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + buf := []byte("dskjahfkdusahfkdsahfkdsafdkas") + for i := 0; i < 10; i++ { + if _, err := stream.Write(buf); err != nil { + t.Fatalf("Error writing to stream: %s", err) + } + } + + // read a bit to make sure a goroutine gets to <-dataChan + if _, err := stream.Read(buf); err != nil { + t.Fatalf("Error reading from stream: %s", err) + } + + // fmt.Printf("Resetting...\n") + if err := stream.Reset(); err != nil { + t.Fatalf("Error reseting stream: %s", err) + } + + closeErr := server.Close() + if closeErr != nil { + t.Fatalf("Error shutting down server: %s", closeErr) + } + wg.Wait() +} + +type roundTripper struct { + conn net.Conn +} + +func (s *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + r := *req + req = &r + + conn, err := net.Dial("tcp", req.URL.Host) + if err != nil { + return nil, err + } + + err = req.Write(conn) + if err != nil { + return nil, err + } + + resp, err := http.ReadResponse(bufio.NewReader(conn), req) + if err != nil { + return nil, err + } + + s.conn = conn + + return resp, nil +} + +// see https://github.com/GoogleCloudPlatform/kubernetes/issues/4882 +func TestFramingAfterRemoteConnectionClosed(t *testing.T) { + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + streamCh := make(chan *Stream) + + w.WriteHeader(http.StatusSwitchingProtocols) + + netconn, _, _ := w.(http.Hijacker).Hijack() + conn, _ := NewConnection(netconn, true) + go conn.Serve(func(s *Stream) { + s.SendReply(http.Header{}, false) + streamCh <- s + }) + + stream := <-streamCh + io.Copy(stream, stream) + + closeChan := make(chan struct{}) + go func() { + stream.Reset() + conn.Close() + close(closeChan) + }() + + <-closeChan + })) + + server.Start() + defer server.Close() + + req, err := http.NewRequest("GET", server.URL, nil) + if err != nil { + t.Fatalf("Error creating request: %s", err) + } + + rt := &roundTripper{} + client := &http.Client{Transport: rt} + + _, err = client.Do(req) + if err != nil { + t.Fatalf("unexpected error from client.Do: %s", err) + } + + conn, err := NewConnection(rt.conn, false) + go conn.Serve(NoOpStreamHandler) + + stream, err := conn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("error creating client stream: %s", err) + } + + n, err := stream.Write([]byte("hello")) + if err != nil { + t.Fatalf("error writing to stream: %s", err) + } + if n != 5 { + t.Fatalf("Expected to write 5 bytes, but actually wrote %d", n) + } + + b := make([]byte, 5) + n, err = stream.Read(b) + if err != nil { + t.Fatalf("error reading from stream: %s", err) + } + if n != 5 { + t.Fatalf("Expected to read 5 bytes, but actually read %d", n) + } + if e, a := "hello", string(b[0:n]); e != a { + t.Fatalf("expected '%s', got '%s'", e, a) + } + + stream.Reset() + conn.Close() +} + +func TestGoAwayRace(t *testing.T) { + var done sync.WaitGroup + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + processDataFrame := make(chan struct{}) + serverClosed := make(chan struct{}) + + done.Add(1) + go func() { + defer done.Done() + serverConn, err := listener.Accept() + if err != nil { + t.Fatalf("Error accepting: %v", err) + } + + serverSpdyConn, err := NewConnection(serverConn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go func() { + <-serverSpdyConn.CloseChan() + close(serverClosed) + }() + + // force the data frame handler to sleep before delivering the frame + serverSpdyConn.dataFrameHandler = func(frame *spdy.DataFrame) error { + <-processDataFrame + return serverSpdyConn.handleDataFrame(frame) + } + + streamCh := make(chan *Stream) + go serverSpdyConn.Serve(func(s *Stream) { + s.SendReply(http.Header{}, false) + streamCh <- s + }) + + stream, ok := <-streamCh + if !ok { + t.Fatalf("didn't get a stream") + } + stream.Close() + data, err := ioutil.ReadAll(stream) + if err != nil { + t.Error(err) + } + if e, a := "hello1hello2hello3hello4hello5", string(data); e != a { + t.Errorf("Expected %q, got %q", e, a) + } + }() + + dialConn, err := net.Dial("tcp", listen) + if err != nil { + t.Fatalf("Error dialing server: %s", err) + } + conn, err := NewConnection(dialConn, false) + if err != nil { + t.Fatalf("Error creating client connectin: %v", err) + } + go conn.Serve(NoOpStreamHandler) + + stream, err := conn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("error creating client stream: %s", err) + } + if err := stream.Wait(); err != nil { + t.Fatalf("error waiting for stream creation: %v", err) + } + + fmt.Fprint(stream, "hello1") + fmt.Fprint(stream, "hello2") + fmt.Fprint(stream, "hello3") + fmt.Fprint(stream, "hello4") + fmt.Fprint(stream, "hello5") + + stream.Close() + conn.Close() + + // wait for the server to get the go away frame + <-serverClosed + + // allow the data frames to be delivered to the server's stream + close(processDataFrame) + + done.Wait() +} + +func TestSetIdleTimeoutAfterRemoteConnectionClosed(t *testing.T) { + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + serverConns := make(chan *Connection, 1) + go func() { + conn, connErr := listener.Accept() + if connErr != nil { + t.Fatal(connErr) + } + serverSpdyConn, err := NewConnection(conn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + serverConns <- serverSpdyConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + if err := spdyConn.Close(); err != nil { + t.Fatal(err) + } + + serverConn := <-serverConns + defer serverConn.Close() + <-serverConn.closeChan + + serverConn.SetIdleTimeout(10 * time.Second) +} + +func TestClientConnectionStopsServingAfterGoAway(t *testing.T) { + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + serverConns := make(chan *Connection, 1) + go func() { + conn, connErr := listener.Accept() + if connErr != nil { + t.Fatal(connErr) + } + serverSpdyConn, err := NewConnection(conn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + serverConns <- serverSpdyConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + if err := stream.WaitTimeout(30 * time.Second); err != nil { + t.Fatalf("Timed out waiting for stream: %v", err) + } + + readChan := make(chan struct{}) + go func() { + _, err := ioutil.ReadAll(stream) + if err != nil { + t.Fatalf("Error reading stream: %v", err) + } + close(readChan) + }() + + serverConn := <-serverConns + serverConn.Close() + + // make sure the client conn breaks out of the main loop in Serve() + <-spdyConn.closeChan + // make sure the remote channels are closed and the stream read is unblocked + <-readChan +} + +func TestStreamReadUnblocksAfterCloseThenReset(t *testing.T) { + listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatalf("Error listening: %v", err) + } + listen := listener.Addr().String() + + serverConns := make(chan *Connection, 1) + go func() { + conn, connErr := listener.Accept() + if connErr != nil { + t.Fatal(connErr) + } + serverSpdyConn, err := NewConnection(conn, true) + if err != nil { + t.Fatalf("Error creating server connection: %v", err) + } + go serverSpdyConn.Serve(NoOpStreamHandler) + serverConns <- serverSpdyConn + }() + + conn, dialErr := net.Dial("tcp", listen) + if dialErr != nil { + t.Fatalf("Error dialing server: %s", dialErr) + } + + spdyConn, spdyErr := NewConnection(conn, false) + if spdyErr != nil { + t.Fatalf("Error creating spdy connection: %s", spdyErr) + } + go spdyConn.Serve(NoOpStreamHandler) + + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + t.Fatalf("Error creating stream: %v", err) + } + if err := stream.WaitTimeout(30 * time.Second); err != nil { + t.Fatalf("Timed out waiting for stream: %v", err) + } + + readChan := make(chan struct{}) + go func() { + _, err := ioutil.ReadAll(stream) + if err != nil { + t.Fatalf("Error reading stream: %v", err) + } + close(readChan) + }() + + serverConn := <-serverConns + defer serverConn.Close() + + if err := stream.Close(); err != nil { + t.Fatal(err) + } + if err := stream.Reset(); err != nil { + t.Fatal(err) + } + + // make sure close followed by reset unblocks stream.Read() + select { + case <-readChan: + case <-time.After(10 * time.Second): + t.Fatal("Timed out waiting for stream read to unblock") + } +} + +var authenticated bool + +func authStreamHandler(stream *Stream) { + if !authenticated { + stream.Refuse() + } + MirrorStreamHandler(stream) +} + +func runServer(wg *sync.WaitGroup) (io.Closer, string, error) { + listener, listenErr := net.Listen("tcp", "localhost:0") + if listenErr != nil { + return nil, "", listenErr + } + wg.Add(1) + go func() { + for { + conn, connErr := listener.Accept() + if connErr != nil { + break + } + + spdyConn, _ := NewConnection(conn, true) + go spdyConn.Serve(authStreamHandler) + + } + wg.Done() + }() + return listener, listener.Addr().String(), nil +} diff --git a/vendor/github.com/docker/spdystream/stream.go b/vendor/github.com/docker/spdystream/stream.go new file mode 100644 index 0000000000..f9e9ee267f --- /dev/null +++ b/vendor/github.com/docker/spdystream/stream.go @@ -0,0 +1,327 @@ +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream/spdy" +) + +var ( + ErrUnreadPartialData = errors.New("unread partial data") +) + +type Stream struct { + streamId spdy.StreamId + parent *Stream + conn *Connection + startChan chan error + + dataLock sync.RWMutex + dataChan chan []byte + unread []byte + + priority uint8 + headers http.Header + headerChan chan http.Header + finishLock sync.Mutex + finished bool + replyCond *sync.Cond + replied bool + closeLock sync.Mutex + closeChan chan bool +} + +// WriteData writes data to stream, sending a dataframe per call +func (s *Stream) WriteData(data []byte, fin bool) error { + s.waitWriteReply() + var flags spdy.DataFlags + + if fin { + flags = spdy.DataFlagFin + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return ErrWriteClosedStream + } + s.finished = true + s.finishLock.Unlock() + } + + dataFrame := &spdy.DataFrame{ + StreamId: s.streamId, + Flags: flags, + Data: data, + } + + debugMessage("(%p) (%d) Writing data frame", s, s.streamId) + return s.conn.framer.WriteFrame(dataFrame) +} + +// Write writes bytes to a stream, calling write data for each call. +func (s *Stream) Write(data []byte) (n int, err error) { + err = s.WriteData(data, false) + if err == nil { + n = len(data) + } + return +} + +// Read reads bytes from a stream, a single read will never get more +// than what is sent on a single data frame, but a multiple calls to +// read may get data from the same data frame. +func (s *Stream) Read(p []byte) (n int, err error) { + if s.unread == nil { + select { + case <-s.closeChan: + return 0, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return 0, io.EOF + } + s.unread = read + } + } + n = copy(p, s.unread) + if n < len(s.unread) { + s.unread = s.unread[n:] + } else { + s.unread = nil + } + return +} + +// ReadData reads an entire data frame and returns the byte array +// from the data frame. If there is unread data from the result +// of a Read call, this function will return an ErrUnreadPartialData. +func (s *Stream) ReadData() ([]byte, error) { + debugMessage("(%p) Reading data from %d", s, s.streamId) + if s.unread != nil { + return nil, ErrUnreadPartialData + } + select { + case <-s.closeChan: + return nil, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return nil, io.EOF + } + return read, nil + } +} + +func (s *Stream) waitWriteReply() { + if s.replyCond != nil { + s.replyCond.L.Lock() + for !s.replied { + s.replyCond.Wait() + } + s.replyCond.L.Unlock() + } +} + +// Wait waits for the stream to receive a reply. +func (s *Stream) Wait() error { + return s.WaitTimeout(time.Duration(0)) +} + +// WaitTimeout waits for the stream to receive a reply or for timeout. +// When the timeout is reached, ErrTimeout will be returned. +func (s *Stream) WaitTimeout(timeout time.Duration) error { + var timeoutChan <-chan time.Time + if timeout > time.Duration(0) { + timeoutChan = time.After(timeout) + } + + select { + case err := <-s.startChan: + if err != nil { + return err + } + break + case <-timeoutChan: + return ErrTimeout + } + return nil +} + +// Close closes the stream by sending an empty data frame with the +// finish flag set, indicating this side is finished with the stream. +func (s *Stream) Close() error { + select { + case <-s.closeChan: + // Stream is now fully closed + s.conn.removeStream(s) + default: + break + } + return s.WriteData([]byte{}, true) +} + +// Reset sends a reset frame, putting the stream into the fully closed state. +func (s *Stream) Reset() error { + s.conn.removeStream(s) + return s.resetStream() +} + +func (s *Stream) resetStream() error { + // Always call closeRemoteChannels, even if s.finished is already true. + // This makes it so that stream.Close() followed by stream.Reset() allows + // stream.Read() to unblock. + s.closeRemoteChannels() + + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return nil + } + s.finished = true + s.finishLock.Unlock() + + resetFrame := &spdy.RstStreamFrame{ + StreamId: s.streamId, + Status: spdy.Cancel, + } + return s.conn.framer.WriteFrame(resetFrame) +} + +// CreateSubStream creates a stream using the current as the parent +func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) { + return s.conn.CreateStream(headers, s, fin) +} + +// SetPriority sets the stream priority, does not affect the +// remote priority of this stream after Open has been called. +// Valid values are 0 through 7, 0 being the highest priority +// and 7 the lowest. +func (s *Stream) SetPriority(priority uint8) { + s.priority = priority +} + +// SendHeader sends a header frame across the stream +func (s *Stream) SendHeader(headers http.Header, fin bool) error { + return s.conn.sendHeaders(headers, s, fin) +} + +// SendReply sends a reply on a stream, only valid to be called once +// when handling a new stream +func (s *Stream) SendReply(headers http.Header, fin bool) error { + if s.replyCond == nil { + return errors.New("cannot reply on initiated stream") + } + s.replyCond.L.Lock() + defer s.replyCond.L.Unlock() + if s.replied { + return nil + } + + err := s.conn.sendReply(headers, s, fin) + if err != nil { + return err + } + + s.replied = true + s.replyCond.Broadcast() + return nil +} + +// Refuse sends a reset frame with the status refuse, only +// valid to be called once when handling a new stream. This +// may be used to indicate that a stream is not allowed +// when http status codes are not being used. +func (s *Stream) Refuse() error { + if s.replied { + return nil + } + s.replied = true + return s.conn.sendReset(spdy.RefusedStream, s) +} + +// Cancel sends a reset frame with the status canceled. This +// can be used at any time by the creator of the Stream to +// indicate the stream is no longer needed. +func (s *Stream) Cancel() error { + return s.conn.sendReset(spdy.Cancel, s) +} + +// ReceiveHeader receives a header sent on the other side +// of the stream. This function will block until a header +// is received or stream is closed. +func (s *Stream) ReceiveHeader() (http.Header, error) { + select { + case <-s.closeChan: + break + case header, ok := <-s.headerChan: + if !ok { + return nil, fmt.Errorf("header chan closed") + } + return header, nil + } + return nil, fmt.Errorf("stream closed") +} + +// Parent returns the parent stream +func (s *Stream) Parent() *Stream { + return s.parent +} + +// Headers returns the headers used to create the stream +func (s *Stream) Headers() http.Header { + return s.headers +} + +// String returns the string version of stream using the +// streamId to uniquely identify the stream +func (s *Stream) String() string { + return fmt.Sprintf("stream:%d", s.streamId) +} + +// Identifier returns a 32 bit identifier for the stream +func (s *Stream) Identifier() uint32 { + return uint32(s.streamId) +} + +// IsFinished returns whether the stream has finished +// sending data +func (s *Stream) IsFinished() bool { + return s.finished +} + +// Implement net.Conn interface + +func (s *Stream) LocalAddr() net.Addr { + return s.conn.conn.LocalAddr() +} + +func (s *Stream) RemoteAddr() net.Addr { + return s.conn.conn.RemoteAddr() +} + +// TODO set per stream values instead of connection-wide + +func (s *Stream) SetDeadline(t time.Time) error { + return s.conn.conn.SetDeadline(t) +} + +func (s *Stream) SetReadDeadline(t time.Time) error { + return s.conn.conn.SetReadDeadline(t) +} + +func (s *Stream) SetWriteDeadline(t time.Time) error { + return s.conn.conn.SetWriteDeadline(t) +} + +func (s *Stream) closeRemoteChannels() { + s.closeLock.Lock() + defer s.closeLock.Unlock() + select { + case <-s.closeChan: + default: + close(s.closeChan) + } +} diff --git a/vendor/github.com/docker/spdystream/utils.go b/vendor/github.com/docker/spdystream/utils.go new file mode 100644 index 0000000000..1b2c199a40 --- /dev/null +++ b/vendor/github.com/docker/spdystream/utils.go @@ -0,0 +1,16 @@ +package spdystream + +import ( + "log" + "os" +) + +var ( + DEBUG = os.Getenv("DEBUG") +) + +func debugMessage(fmt string, args ...interface{}) { + if DEBUG != "" { + log.Printf(fmt, args...) + } +} diff --git a/vendor/github.com/docker/spdystream/ws/connection.go b/vendor/github.com/docker/spdystream/ws/connection.go new file mode 100644 index 0000000000..d0ea001b45 --- /dev/null +++ b/vendor/github.com/docker/spdystream/ws/connection.go @@ -0,0 +1,65 @@ +package ws + +import ( + "github.com/gorilla/websocket" + "io" + "log" + "time" +) + +// Wrap an HTTP2 connection over WebSockets and +// use the underlying WebSocket framing for proxy +// compatibility. +type Conn struct { + *websocket.Conn + reader io.Reader +} + +func NewConnection(w *websocket.Conn) *Conn { + return &Conn{Conn: w} +} + +func (c Conn) Write(b []byte) (int, error) { + err := c.WriteMessage(websocket.BinaryMessage, b) + if err != nil { + return 0, err + } + return len(b), nil +} + +func (c Conn) Read(b []byte) (int, error) { + if c.reader == nil { + t, r, err := c.NextReader() + if err != nil { + return 0, err + } + if t != websocket.BinaryMessage { + log.Printf("ws: ignored non-binary message in stream") + return 0, nil + } + c.reader = r + } + n, err := c.reader.Read(b) + if err != nil { + if err == io.EOF { + c.reader = nil + } + return n, err + } + return n, nil +} + +func (c Conn) SetDeadline(t time.Time) error { + if err := c.Conn.SetReadDeadline(t); err != nil { + return err + } + if err := c.Conn.SetWriteDeadline(t); err != nil { + return err + } + return nil +} + +func (c Conn) Close() error { + err := c.Conn.Close() + return err +} diff --git a/vendor/github.com/docker/spdystream/ws/ws_test.go b/vendor/github.com/docker/spdystream/ws/ws_test.go new file mode 100644 index 0000000000..36c4a46ac8 --- /dev/null +++ b/vendor/github.com/docker/spdystream/ws/ws_test.go @@ -0,0 +1,175 @@ +package ws + +import ( + "bytes" + "github.com/docker/spdystream" + "github.com/gorilla/websocket" + "io" + "log" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +var serverSpdyConn *spdystream.Connection + +// Connect to the Websocket endpoint at ws://localhost +// using SPDY over Websockets framing. +func ExampleConn() { + wsconn, _, _ := websocket.DefaultDialer.Dial("ws://localhost/", http.Header{"Origin": {"http://localhost/"}}) + conn, _ := spdystream.NewConnection(NewConnection(wsconn), false) + go conn.Serve(spdystream.NoOpStreamHandler, spdystream.NoAuthHandler) + stream, _ := conn.CreateStream(http.Header{}, nil, false) + stream.Wait() +} + +func serveWs(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + log.Println(err) + } + return + } + + wrap := NewConnection(ws) + spdyConn, err := spdystream.NewConnection(wrap, true) + if err != nil { + log.Fatal(err) + return + } + serverSpdyConn = spdyConn + go spdyConn.Serve(spdystream.MirrorStreamHandler, authStreamHandler) +} + +func TestSpdyStreamOverWs(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(serveWs)) + defer server.Close() + defer func() { + if serverSpdyConn != nil { + serverSpdyConn.Close() + } + }() + + wsconn, _, err := websocket.DefaultDialer.Dial(strings.Replace(server.URL, "http://", "ws://", 1), http.Header{"Origin": {server.URL}}) + if err != nil { + t.Fatal(err) + } + + wrap := NewConnection(wsconn) + spdyConn, err := spdystream.NewConnection(wrap, false) + if err != nil { + defer wsconn.Close() + t.Fatal(err) + } + defer spdyConn.Close() + authenticated = true + go spdyConn.Serve(spdystream.NoOpStreamHandler, spdystream.RejectAuthHandler) + + stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if streamErr != nil { + t.Fatalf("Error creating stream: %s", streamErr) + } + + waitErr := stream.Wait() + if waitErr != nil { + t.Fatalf("Error waiting for stream: %s", waitErr) + } + + message := []byte("hello") + writeErr := stream.WriteData(message, false) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + buf := make([]byte, 10) + n, readErr := stream.Read(buf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 5 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n) + } + if bytes.Compare(buf[:n], message) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message) + } + + writeErr = stream.WriteData(message, true) + if writeErr != nil { + t.Fatalf("Error writing data") + } + + smallBuf := make([]byte, 3) + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 3 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n) + } + if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message) + } + n, readErr = stream.Read(smallBuf) + if readErr != nil { + t.Fatalf("Error reading data from stream: %s", readErr) + } + if n != 2 { + t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n) + } + if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 { + t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n]) + } + + n, readErr = stream.Read(buf) + if readErr != io.EOF { + t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n) + } + + streamCloseErr := stream.Close() + if streamCloseErr != nil { + t.Fatalf("Error closing stream: %s", streamCloseErr) + } + + // Closing again should return nil + streamCloseErr = stream.Close() + if streamCloseErr != nil { + t.Fatalf("Error closing stream: %s", streamCloseErr) + } + + authenticated = false + badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false) + if badStreamErr != nil { + t.Fatalf("Error creating stream: %s", badStreamErr) + } + + waitErr = badStream.Wait() + if waitErr == nil { + t.Fatalf("Did not receive error creating stream") + } + if waitErr != spdystream.ErrReset { + t.Fatalf("Unexpected error creating stream: %s", waitErr) + } + + spdyCloseErr := spdyConn.Close() + if spdyCloseErr != nil { + t.Fatalf("Error closing spdy connection: %s", spdyCloseErr) + } +} + +var authenticated bool + +func authStreamHandler(header http.Header, slot uint8, parent uint32) bool { + return authenticated +} diff --git a/vendor/github.com/renstrom/dedent/.travis.yml b/vendor/github.com/renstrom/dedent/.travis.yml new file mode 100644 index 0000000000..e61f42b872 --- /dev/null +++ b/vendor/github.com/renstrom/dedent/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.0 + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + +sudo: false diff --git a/vendor/github.com/renstrom/dedent/LICENSE b/vendor/github.com/renstrom/dedent/LICENSE new file mode 100644 index 0000000000..66a9870fcf --- /dev/null +++ b/vendor/github.com/renstrom/dedent/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Peter Renström + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/renstrom/dedent/README.md b/vendor/github.com/renstrom/dedent/README.md new file mode 100644 index 0000000000..35b5aa1341 --- /dev/null +++ b/vendor/github.com/renstrom/dedent/README.md @@ -0,0 +1,50 @@ +# Dedent + +[![Build Status](https://travis-ci.org/renstrom/dedent.svg?branch=master)](https://travis-ci.org/renstrom/dedent) +[![Godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/renstrom/dedent) + +Removes common leading whitespace from multiline strings. Inspired by [`textwrap.dedent`](https://docs.python.org/3/library/textwrap.html#textwrap.dedent) in Python. + +## Usage / example + +Imagine the following snippet that prints a multiline string. You want the indentation to both look nice in the code as well as in the actual output. + +```go +package main + +import ( + "fmt" + + "github.com/renstrom/dedent" +) + +func main() { + s := `Lorem ipsum dolor sit amet, + consectetur adipiscing elit. + Curabitur justo tellus, facilisis nec efficitur dictum, + fermentum vitae ligula. Sed eu convallis sapien.` + fmt.Println(dedent.Dedent(s)) + fmt.Println("-------------") + fmt.Println(s) +} +``` + +To illustrate the difference, here's the output: + + +```bash +$ go run main.go +Lorem ipsum dolor sit amet, +consectetur adipiscing elit. +Curabitur justo tellus, facilisis nec efficitur dictum, +fermentum vitae ligula. Sed eu convallis sapien. +------------- +Lorem ipsum dolor sit amet, + consectetur adipiscing elit. + Curabitur justo tellus, facilisis nec efficitur dictum, + fermentum vitae ligula. Sed eu convallis sapien. +``` + +## License + +MIT diff --git a/vendor/github.com/renstrom/dedent/dedent.go b/vendor/github.com/renstrom/dedent/dedent.go new file mode 100644 index 0000000000..9d5bfbabd1 --- /dev/null +++ b/vendor/github.com/renstrom/dedent/dedent.go @@ -0,0 +1,49 @@ +package dedent + +import ( + "regexp" + "strings" +) + +var ( + whitespaceOnly = regexp.MustCompile("(?m)^[ \t]+$") + leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])") +) + +// Dedent removes any common leading whitespace from every line in text. +// +// This can be used to make multiline strings to line up with the left edge of +// the display, while still presenting them in the source code in indented +// form. +func Dedent(text string) string { + var margin string + + text = whitespaceOnly.ReplaceAllString(text, "") + indents := leadingWhitespace.FindAllStringSubmatch(text, -1) + + // Look for the longest leading string of spaces and tabs common to all + // lines. + for i, indent := range indents { + if i == 0 { + margin = indent[1] + } else if strings.HasPrefix(indent[1], margin) { + // Current line more deeply indented than previous winner: + // no change (previous winner is still on top). + continue + } else if strings.HasPrefix(margin, indent[1]) { + // Current line consistent with and no deeper than previous winner: + // it's the new winner. + margin = indent[1] + } else { + // Current line and previous winner have no common whitespace: + // there is no margin. + margin = "" + break + } + } + + if margin != "" { + text = regexp.MustCompile("(?m)^"+margin).ReplaceAllString(text, "") + } + return text +} diff --git a/vendor/github.com/renstrom/dedent/dedent_test.go b/vendor/github.com/renstrom/dedent/dedent_test.go new file mode 100644 index 0000000000..dc5d11f4da --- /dev/null +++ b/vendor/github.com/renstrom/dedent/dedent_test.go @@ -0,0 +1,169 @@ +package dedent + +import ( + "fmt" + "testing" +) + +const errorMsg = "\nexpected %q\ngot %q" + +type dedentTest struct { + text, expect string +} + +func TestDedentNoMargin(t *testing.T) { + texts := []string{ + // No lines indented + "Hello there.\nHow are you?\nOh good, I'm glad.", + // Similar with a blank line + "Hello there.\n\nBoo!", + // Some lines indented, but overall margin is still zero + "Hello there.\n This is indented.", + // Again, add a blank line. + "Hello there.\n\n Boo!\n", + } + + for _, text := range texts { + if text != Dedent(text) { + t.Errorf(errorMsg, text, Dedent(text)) + } + } +} + +func TestDedentEven(t *testing.T) { + texts := []dedentTest{ + { + // All lines indented by two spaces + text: " Hello there.\n How are ya?\n Oh good.", + expect: "Hello there.\nHow are ya?\nOh good.", + }, + { + // Same, with blank lines + text: " Hello there.\n\n How are ya?\n Oh good.\n", + expect: "Hello there.\n\nHow are ya?\nOh good.\n", + }, + { + // Now indent one of the blank lines + text: " Hello there.\n \n How are ya?\n Oh good.\n", + expect: "Hello there.\n\nHow are ya?\nOh good.\n", + }, + } + + for _, text := range texts { + if text.expect != Dedent(text.text) { + t.Errorf(errorMsg, text.expect, Dedent(text.text)) + } + } +} + +func TestDedentUneven(t *testing.T) { + texts := []dedentTest{ + { + // Lines indented unevenly + text: ` + def foo(): + while 1: + return foo + `, + expect: ` +def foo(): + while 1: + return foo +`, + }, + { + // Uneven indentation with a blank line + text: " Foo\n Bar\n\n Baz\n", + expect: "Foo\n Bar\n\n Baz\n", + }, + { + // Uneven indentation with a whitespace-only line + text: " Foo\n Bar\n \n Baz\n", + expect: "Foo\n Bar\n\n Baz\n", + }, + } + + for _, text := range texts { + if text.expect != Dedent(text.text) { + t.Errorf(errorMsg, text.expect, Dedent(text.text)) + } + } +} + +// Dedent() should not mangle internal tabs. +func TestDedentPreserveInternalTabs(t *testing.T) { + text := " hello\tthere\n how are\tyou?" + expect := "hello\tthere\nhow are\tyou?" + if expect != Dedent(text) { + t.Errorf(errorMsg, expect, Dedent(text)) + } + + // Make sure that it preserves tabs when it's not making any changes at all + if expect != Dedent(expect) { + t.Errorf(errorMsg, expect, Dedent(expect)) + } +} + +// Dedent() should not mangle tabs in the margin (i.e. tabs and spaces both +// count as margin, but are *not* considered equivalent). +func TestDedentPreserveMarginTabs(t *testing.T) { + texts := []string{ + " hello there\n\thow are you?", + // Same effect even if we have 8 spaces + " hello there\n\thow are you?", + } + + for _, text := range texts { + d := Dedent(text) + if text != d { + t.Errorf(errorMsg, text, d) + } + } + + texts2 := []dedentTest{ + { + // Dedent() only removes whitespace that can be uniformly removed! + text: "\thello there\n\thow are you?", + expect: "hello there\nhow are you?", + }, + { + text: " \thello there\n \thow are you?", + expect: "hello there\nhow are you?", + }, + { + text: " \t hello there\n \t how are you?", + expect: "hello there\nhow are you?", + }, + { + text: " \thello there\n \t how are you?", + expect: "hello there\n how are you?", + }, + } + + for _, text := range texts2 { + if text.expect != Dedent(text.text) { + t.Errorf(errorMsg, text.expect, Dedent(text.text)) + } + } +} + +func ExampleDedent() { + fmt.Println(Dedent(` + Lorem ipsum dolor sit amet, + consectetur adipiscing elit. + Curabitur justo tellus, facilisis nec efficitur dictum, + fermentum vitae ligula. Sed eu convallis sapien.`)) + // Output: + // Lorem ipsum dolor sit amet, + // consectetur adipiscing elit. + // Curabitur justo tellus, facilisis nec efficitur dictum, + // fermentum vitae ligula. Sed eu convallis sapien. +} + +func BenchmarkDedent(b *testing.B) { + for i := 0; i < b.N; i++ { + Dedent(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Curabitur justo tellus, facilisis nec efficitur dictum, + fermentum vitae ligula. Sed eu convallis sapien.`) + } +} diff --git a/vendor/k8s.io/heapster/.gitignore b/vendor/k8s.io/heapster/.gitignore new file mode 100644 index 0000000000..39dd0018c8 --- /dev/null +++ b/vendor/k8s.io/heapster/.gitignore @@ -0,0 +1,10 @@ +heapster +eventer +.cover + +# Vim-related files +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist diff --git a/vendor/k8s.io/heapster/.travis.yml b/vendor/k8s.io/heapster/.travis.yml new file mode 100644 index 0000000000..9ac1ff2e8b --- /dev/null +++ b/vendor/k8s.io/heapster/.travis.yml @@ -0,0 +1,16 @@ +sudo: false + +language: go + +go_import_path: k8s.io/heapster + +go: + - 1.6 + +install: + - go get -u github.com/axw/gocov/gocov + - go get -u golang.org/x/tools/cmd/cover + - go get -u github.com/mattn/goveralls + +script: + - make test-unit test-unit-cov diff --git a/vendor/k8s.io/heapster/CONTRIBUTING.md b/vendor/k8s.io/heapster/CONTRIBUTING.md new file mode 100644 index 0000000000..1010ad08c8 --- /dev/null +++ b/vendor/k8s.io/heapster/CONTRIBUTING.md @@ -0,0 +1,14 @@ +## How to contribute + +Open an issue or a pull request, it's that easy! + +## Contributor License Agreements + +We'd love to accept your pull requests! Before we can merge them, we have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement (CLA). + + * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html). + * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html). + +Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. diff --git a/vendor/k8s.io/heapster/Godeps/Godeps.json b/vendor/k8s.io/heapster/Godeps/Godeps.json new file mode 100644 index 0000000000..86ba8a8cbc --- /dev/null +++ b/vendor/k8s.io/heapster/Godeps/Godeps.json @@ -0,0 +1,953 @@ +{ + "ImportPath": "k8s.io/heapster", + "GoVersion": "go1.6", + "GodepVersion": "v74", + "Packages": [ + "./..." + ], + "Deps": [ + { + "ImportPath": "github.com/beorn7/perks/quantile", + "Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4" + }, + { + "ImportPath": "github.com/bigdatadev/goryman", + "Rev": "55c3cbc3df542201b3c140e72a758d0adae4bc05" + }, + { + "ImportPath": "github.com/bigdatadev/goryman/proto", + "Rev": "55c3cbc3df542201b3c140e72a758d0adae4bc05" + }, + { + "ImportPath": "github.com/blang/semver", + "Comment": "v3.0.1", + "Rev": "31b736133b98f26d5e078ec9eb591666edfd091f" + }, + { + "ImportPath": "github.com/bluebreezecf/opentsdb-goclient/client", + "Rev": "539764bd9a9cae6632dea52b5a587451728a3983" + }, + { + "ImportPath": "github.com/bluebreezecf/opentsdb-goclient/config", + "Rev": "539764bd9a9cae6632dea52b5a587451728a3983" + }, + { + "ImportPath": "github.com/coreos/go-oidc/http", + "Rev": "5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b" + }, + { + "ImportPath": "github.com/coreos/go-oidc/jose", + "Rev": "5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b" + }, + { + "ImportPath": "github.com/coreos/go-oidc/key", + "Rev": "5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b" + }, + { + "ImportPath": "github.com/coreos/go-oidc/oauth2", + "Rev": "5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b" + }, + { + "ImportPath": "github.com/coreos/go-oidc/oidc", + "Rev": "5cf2aa52da8c574d3aa4458f471ad6ae2240fe6b" + }, + { + "ImportPath": "github.com/coreos/go-systemd/journal", + "Comment": "v8-2-g4484981", + "Rev": "4484981625c1a6a2ecb40a390fcb6a9bcfee76e3" + }, + { + "ImportPath": "github.com/coreos/pkg/capnslog", + "Comment": "v2", + "Rev": "7f080b6c11ac2d2347c3cd7521e810207ea1a041" + }, + { + "ImportPath": "github.com/coreos/pkg/health", + "Comment": "v2", + "Rev": "7f080b6c11ac2d2347c3cd7521e810207ea1a041" + }, + { + "ImportPath": "github.com/coreos/pkg/httputil", + "Comment": "v2", + "Rev": "7f080b6c11ac2d2347c3cd7521e810207ea1a041" + }, + { + "ImportPath": "github.com/coreos/pkg/timeutil", + "Comment": "v2", + "Rev": "7f080b6c11ac2d2347c3cd7521e810207ea1a041" + }, + { + "ImportPath": "github.com/davecgh/go-spew/spew", + "Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d" + }, + { + "ImportPath": "github.com/docker/distribution/digest", + "Comment": "v2.4.0-rc.1-38-gcd27f17", + "Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51" + }, + { + "ImportPath": "github.com/docker/distribution/reference", + "Comment": "v2.4.0-rc.1-38-gcd27f17", + "Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51" + }, + { + "ImportPath": "github.com/emicklei/go-restful", + "Comment": "v1.2-66-gc4afa8e", + "Rev": "c4afa8e5421428d584b32d36d7b35f2c9ae5f823" + }, + { + "ImportPath": "github.com/emicklei/go-restful/log", + "Comment": "v1.2-66-gc4afa8e", + "Rev": "c4afa8e5421428d584b32d36d7b35f2c9ae5f823" + }, + { + "ImportPath": "github.com/emicklei/go-restful/swagger", + "Comment": "v1.2-66-gc4afa8e", + "Rev": "c4afa8e5421428d584b32d36d7b35f2c9ae5f823" + }, + { + "ImportPath": "github.com/ghodss/yaml", + "Rev": "73d445a93680fa1a78ae23a5839bad48f32ba1ee" + }, + { + "ImportPath": "github.com/gogo/protobuf/proto", + "Comment": "v0.1-125-g82d16f7", + "Rev": "82d16f734d6d871204a3feb1a73cb220cc92574c" + }, + { + "ImportPath": "github.com/gogo/protobuf/sortkeys", + "Comment": "v0.1-125-g82d16f7", + "Rev": "82d16f734d6d871204a3feb1a73cb220cc92574c" + }, + { + "ImportPath": "github.com/golang/glog", + "Rev": "44145f04b68cf362d9c4df2182967c2275eaefed" + }, + { + "ImportPath": "github.com/golang/protobuf/proto", + "Rev": "b982704f8bb716bb608144408cff30e15fbde841" + }, + { + "ImportPath": "github.com/golang/snappy", + "Rev": "723cc1e459b8eea2dea4583200fd60757d40097a" + }, + { + "ImportPath": "github.com/google/cadvisor/info/v1", + "Comment": "v0.23.2-79-gc6c06d4", + "Rev": "c6c06d440ab2fcaae9211dda6dcbbaa1e98a054b" + }, + { + "ImportPath": "github.com/google/gofuzz", + "Rev": "bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5" + }, + { + "ImportPath": "github.com/hawkular/hawkular-client-go/metrics", + "Comment": "v0.6.0", + "Rev": "50f4099dfd739f913ed7d8f33288aeeca338516a" + }, + { + "ImportPath": "github.com/imdario/mergo", + "Comment": "0.1.3-8-g6633656", + "Rev": "6633656539c1639d9d78127b7d47c622b5d7b6dc" + }, + { + "ImportPath": "github.com/influxdata/influxdb/client", + "Comment": "v0.12.2", + "Rev": "383332daed5595926c235f250b11433f67229c35" + }, + { + "ImportPath": "github.com/influxdata/influxdb/models", + "Comment": "v0.12.2", + "Rev": "383332daed5595926c235f250b11433f67229c35" + }, + { + "ImportPath": "github.com/influxdata/influxdb/pkg/escape", + "Comment": "v0.12.2", + "Rev": "383332daed5595926c235f250b11433f67229c35" + }, + { + "ImportPath": "github.com/jonboulle/clockwork", + "Rev": "3f831b65b61282ba6bece21b91beea2edc4c887a" + }, + { + "ImportPath": "github.com/juju/ratelimit", + "Rev": "77ed1c8a01217656d2080ad51981f6e99adaa177" + }, + { + "ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil", + "Rev": "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a" + }, + { + "ImportPath": "github.com/mitchellh/mapstructure", + "Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf" + }, + { + "ImportPath": "github.com/olivere/elastic", + "Comment": "v3.0.41", + "Rev": "de60371c6ac39d035d1db8d64e6ae66552536950" + }, + { + "ImportPath": "github.com/optiopay/kafka", + "Rev": "bc8e0950689f19fafd454acd517abe7b251cf54f" + }, + { + "ImportPath": "github.com/optiopay/kafka/proto", + "Rev": "bc8e0950689f19fafd454acd517abe7b251cf54f" + }, + { + "ImportPath": "github.com/pborman/uuid", + "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" + }, + { + "ImportPath": "github.com/pmezard/go-difflib/difflib", + "Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d" + }, + { + "ImportPath": "github.com/prometheus/client_golang/prometheus", + "Comment": "0.7.0-39-g3b78d7a", + "Rev": "3b78d7a77f51ccbc364d4bc170920153022cfd08" + }, + { + "ImportPath": "github.com/prometheus/client_model/go", + "Comment": "model-0.0.2-12-gfa8ad6f", + "Rev": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6" + }, + { + "ImportPath": "github.com/prometheus/common/expfmt", + "Rev": "a6ab08426bb262e2d190097751f5cfd1cfdfd17d" + }, + { + "ImportPath": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg", + "Rev": "a6ab08426bb262e2d190097751f5cfd1cfdfd17d" + }, + { + "ImportPath": "github.com/prometheus/common/model", + "Rev": "a6ab08426bb262e2d190097751f5cfd1cfdfd17d" + }, + { + "ImportPath": "github.com/prometheus/procfs", + "Rev": "490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d" + }, + { + "ImportPath": "github.com/rackspace/gophercloud", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tenants", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v2/tokens", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v3/services", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack/identity/v3/tokens", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/openstack/utils", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/pagination", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/testhelper", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/rackspace/gophercloud/testhelper/client", + "Comment": "v1.0.0-920-g934dbf8", + "Rev": "934dbf81977c67c521c75492dc1f55ca74dc5b04" + }, + { + "ImportPath": "github.com/spf13/pflag", + "Rev": "08b1a584251b5b62f458943640fc8ebd4d50aaa5" + }, + { + "ImportPath": "github.com/stretchr/objx", + "Rev": "1a9d0bb9f541897e62256577b352fdbc1fb4fd94" + }, + { + "ImportPath": "github.com/stretchr/testify/assert", + "Rev": "2160c81a963bc827f650a9d420263d05d35bb97e" + }, + { + "ImportPath": "github.com/stretchr/testify/mock", + "Rev": "2160c81a963bc827f650a9d420263d05d35bb97e" + }, + { + "ImportPath": "github.com/stretchr/testify/require", + "Rev": "2160c81a963bc827f650a9d420263d05d35bb97e" + }, + { + "ImportPath": "github.com/ugorji/go/codec", + "Rev": "f4485b318aadd133842532f841dc205a8e339d74" + }, + { + "ImportPath": "golang.org/x/net/context", + "Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0" + }, + { + "ImportPath": "golang.org/x/net/context/ctxhttp", + "Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0" + }, + { + "ImportPath": "golang.org/x/net/http2", + "Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0" + }, + { + "ImportPath": "golang.org/x/net/http2/hpack", + "Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0" + }, + { + "ImportPath": "golang.org/x/net/lex/httplex", + "Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0" + }, + { + "ImportPath": "golang.org/x/oauth2", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/google", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/internal", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/jws", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "golang.org/x/oauth2/jwt", + "Rev": "b5adcc2dcdf009d0391547edc6ecbaff889f5bb9" + }, + { + "ImportPath": "google.golang.org/api/cloudmonitoring/v2beta2", + "Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037" + }, + { + "ImportPath": "google.golang.org/api/gensupport", + "Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037" + }, + { + "ImportPath": "google.golang.org/api/googleapi", + "Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037" + }, + { + "ImportPath": "google.golang.org/api/googleapi/internal/uritemplates", + "Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037" + }, + { + "ImportPath": "google.golang.org/api/logging/v2beta1", + "Rev": "4300f6b0c8a7f09e521dd0af2cee27e28846e037" + }, + { + "ImportPath": "google.golang.org/cloud/compute/metadata", + "Rev": "eb47ba841d53d93506cfbfbc03927daf9cc48f88" + }, + { + "ImportPath": "google.golang.org/cloud/internal", + "Rev": "eb47ba841d53d93506cfbfbc03927daf9cc48f88" + }, + { + "ImportPath": "gopkg.in/inf.v0", + "Comment": "v0.9.0", + "Rev": "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" + }, + { + "ImportPath": "gopkg.in/olivere/elastic.v3/backoff", + "Comment": "v3.0.41", + "Rev": "de60371c6ac39d035d1db8d64e6ae66552536950" + }, + { + "ImportPath": "gopkg.in/olivere/elastic.v3/uritemplates", + "Comment": "v3.0.41", + "Rev": "de60371c6ac39d035d1db8d64e6ae66552536950" + }, + { + "ImportPath": "gopkg.in/yaml.v2", + "Rev": "a83829b6f1293c91addabc89d0571c246397bbf4" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/endpoints", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/errors", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/meta", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/meta/metatypes", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/pod", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/resource", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/service", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/unversioned", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/unversioned/validation", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/util", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/v1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/api/validation", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apimachinery", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apimachinery/registered", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/apps", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/apps/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/apps/v1alpha1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authentication", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authentication/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authentication/v1beta1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authorization", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authorization/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/autoscaling/v1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch/v1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/certificates", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/certificates/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/extensions", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/extensions/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/policy", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/policy/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/policy/v1alpha1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/rbac", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/rbac/install", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/auth/authenticator", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/auth/user", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/capabilities", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/cache", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/metrics", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/restclient", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/transport", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/typed/discovery", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/auth", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/conversion", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/conversion/queryparams", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/fields", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubelet/client", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubelet/qos", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/kubelet/types", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/labels", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/master/ports", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/json", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/protobuf", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/recognizer", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/streaming", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/runtime/serializer/versioning", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/types", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/clock", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/config", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/crypto", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/errors", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/flowcontrol", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/framer", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/hash", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/homedir", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/integer", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/intstr", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/json", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/labels", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/net", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/net/sets", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/parsers", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/rand", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/runtime", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/sets", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/testing", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/uuid", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/validation", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/validation/field", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/wait", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/util/yaml", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/version", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/watch", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/pkg/watch/versioned", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/plugin/pkg/client/auth", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/plugin/pkg/client/auth/gcp", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/plugin/pkg/client/auth/oidc", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + }, + { + "ImportPath": "k8s.io/kubernetes/third_party/forked/golang/reflect", + "Comment": "v1.4.0-alpha.2-552-g301be4e", + "Rev": "301be4eeb561fc9d5834a6d5b1e3cbf65091b302" + } + ] +} diff --git a/vendor/k8s.io/heapster/Godeps/Readme b/vendor/k8s.io/heapster/Godeps/Readme new file mode 100644 index 0000000000..4cdaa53d56 --- /dev/null +++ b/vendor/k8s.io/heapster/Godeps/Readme @@ -0,0 +1,5 @@ +This directory tree is generated automatically by godep. + +Please do not edit. + +See https://github.com/tools/godep for more information. diff --git a/vendor/k8s.io/heapster/LICENSE b/vendor/k8s.io/heapster/LICENSE new file mode 100644 index 0000000000..5c304d1a4a --- /dev/null +++ b/vendor/k8s.io/heapster/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/k8s.io/heapster/Makefile b/vendor/k8s.io/heapster/Makefile new file mode 100644 index 0000000000..f1cec9a03e --- /dev/null +++ b/vendor/k8s.io/heapster/Makefile @@ -0,0 +1,49 @@ +all: build + +TAG = v1.2.0-beta.1 +PREFIX = gcr.io/google_containers +FLAGS = + +SUPPORTED_KUBE_VERSIONS = "1.2.4" +TEST_NAMESPACE = heapster-e2e-tests + +deps: + go get github.com/tools/godep + +build: clean deps + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build ./... + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build -o heapster k8s.io/heapster/metrics + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build -o eventer k8s.io/heapster/events + +sanitize: + hooks/check_boilerplate.sh + hooks/check_gofmt.sh + hooks/run_vet.sh + +test-unit: clean deps sanitize build + GOOS=linux GOARCH=amd64 godep go test --test.short -race ./... $(FLAGS) + +test-unit-cov: clean deps sanitize build + hooks/coverage.sh + +test-integration: clean deps build + godep go test -v --timeout=30m ./integration/... --vmodule=*=2 $(FLAGS) --namespace=$(TEST_NAMESPACE) --kube_versions=$(SUPPORTED_KUBE_VERSIONS) + +container: build + cp heapster deploy/docker/heapster + cp eventer deploy/docker/eventer + docker build -t $(PREFIX)/heapster:$(TAG) deploy/docker/ + +grafana: + docker build -t $(PREFIX)/heapster_grafana:$(TAG) grafana/ + +influxdb: + docker build -t $(PREFIX)/heapster_influxdb:$(TAG) influxdb/ + +clean: + rm -f heapster + rm -f eventer + rm -f deploy/docker/heapster + rm -f deploy/docker/eventer + +.PHONY: all deps build sanitize test-unit test-unit-cov test-integration container grafana influxdb clean diff --git a/vendor/k8s.io/heapster/README.md b/vendor/k8s.io/heapster/README.md new file mode 100755 index 0000000000..b5e8349c89 --- /dev/null +++ b/vendor/k8s.io/heapster/README.md @@ -0,0 +1,40 @@ +# Heapster + +[![GoDoc](https://godoc.org/k8s.io/heapster?status.svg)](https://godoc.org/k8s.io/heapster) [![Build Status](https://travis-ci.org/kubernetes/heapster.svg?branch=master)](https://travis-ci.org/kubernetes/heapster) + +Heapster enables Container Cluster Monitoring and Performance Analysis. + +Heapster currently supports [Kubernetes](https://github.com/kubernetes/kubernetes) and CoreOS natively. +*Heapster is compatible with kubernetes versions starting from v1.0.6 only* + +It can be extended to support other cluster management solutions easily. + +Heapster collects and interprets various signals like compute resource usage, lifecycle events, etc, and exports cluster metrics via [REST endpoints](docs/model.md). +**Note: Some of the endpoints are only valid in Kubernetes clusters** + +Use [Kubedash](https://github.com/kubernetes/kubedash) to visualize data exported by Heapster. + +Heapster supports multiple sources of data. +More information [here](docs/source-configuration.md). + +Heapster supports a pluggable storage backend. +It supports [InfluxDB](http://influxdb.com) with [Grafana](http://grafana.org/docs/features/influxdb), [Google Cloud Monitoring](https://cloud.google.com/monitoring/), [Google Cloud Logging](https://cloud.google.com/logging/), [Hawkular](http://www.hawkular.org), [Riemann](http://riemann.io) and [Kafka](http://kafka.apache.org/). +We welcome patches that add additional storage backends. +Documentation on storage sinks [here](docs/sink-configuration.md) +The current version of Storage Schema is documented [here](docs/storage-schema.md). + +### Running Heapster on Kubernetes + +To run Heapster on a Kubernetes cluster with, +- InfluxDB use [this guide](docs/influxdb.md). +- Google Cloud Monitoring and Google Cloud Logging use [this guide](docs/google.md). + +### Running Heapster on OpenShift + +Using Heapster to monitor an OpenShift cluster requires some additional changes to the Kubernetes instructions to allow communication between the Heapster instance and OpenShift's secured endpoints. To run standalone Heapster or a combination of Heapster and Hawkular-Metrics in OpenShift, follow [this guide](https://github.com/openshift/origin-metrics). + +#### Troubleshooting guide [here](docs/debugging.md) + +### Community + +Contributions, questions, and comments are all welcomed and encouraged! Heapster and cAdvisor developers hang out in the [#google-containers](http://webchat.freenode.net/?channels=google-containers) room on freenode.net. You can also reach us on the [google-containers Google Groups mailing list](https://groups.google.com/forum/#!forum/google-containers). diff --git a/vendor/k8s.io/heapster/RELEASES.md b/vendor/k8s.io/heapster/RELEASES.md new file mode 100644 index 0000000000..aa0a30f6ff --- /dev/null +++ b/vendor/k8s.io/heapster/RELEASES.md @@ -0,0 +1,125 @@ +# Release Notes + +## 0.19.1 (15-12-2015) +- Better connection handling for InfluxDB. +- Refresh node and pod lists every 1 hour. + +## 0.19.0 (9-12-2015) +- Switched to InfluxDB 0.9. The data layout was changed. +- Added Kafka and Riemann sinks. +- Removed poll_duration flag. +- Authentication and security improvements. +- Fixed issue with unavailable sink. + +## 0.18.1 (9-18-2015) +- Avoid using UIDs for Kubernetes events. +- Export container labels. +- Hawkular sink upgrades + +## 0.18.0 +- Cluster Model APIs enabled by default +- Garbage collection in the cluster model +- New rolling window based bucketing storage engine for model that significantly reduces memory footprint +- Many fixes around race conditions with the cache +- GCP authorization policy updated to reduce flakiness + +## 0.17.0 +- Fixes service account handling. +- New APIs that provide insight into a kubernetes cluster - usage distribution across nodes, namespaces, pods, etc. + +## 0.16.0 (7-7-2015) +- Add new sink for GCM autoscaling. +- Force heapster to use kubernetes v1.0 API. +- Bug fixes. + +## 0.15.0 (6-26-2015) +- Use service accounts while running in kubernetes clusters +- Expose kubernetes system containers with standard names +- Minor bug fixes. + +## 0.14.3 (6-19-2015) +- Expose HostID for nodes. +- Added an API '/metric-export-schema' that exposes the schema for '/metric-export' API. + +## 0.14.2 (6-16-2015) +- Expose HostID for all containers. + +## 0.14.1 (6-15-2015) +- Expose External ID of nodes as a label and namespace UID as a label. +- Fix bug in handling metric specific labels. + +## 0.14.0 (6-9-2015) +- Heapster exposes a REST endpoint that serves the metrics being pushed to sinks. +- Support for service accounts. + +## 0.13.0 (5-29-2015) +- Switch use of Kubernetes API to v1beta3. +- Add option to connect to Kubelets through HTTPS. + +## 0.12.1 (5-13-2015) +- Fixes kube master url handling via --source flag. + +## 0.12.0 (5-12-2015) +- Fixes issues related to supporting multiple sinks. +- Resource usage of kubernetes system daemons are exported. +- Support for kubernetes specific auth (secrets). +- Scalability improvements. + +## 0.11.0 (4-28-2015) +- Export filesystem usage metrics for the node and containers. +- Support for Kubernetes events +- New Sink - Google Cloud Logging. Supports events only. +- New Sink - Google Cloud Monitoring. Supports metrics only. +- New metric labels - 'pod_name' and 'resource_id'. +- Extensible source and sinks configuration. It is now possible to export metrics and events to multiple sinks simultaneously. + +## 0.10.0 (3-30-2015) +- Downsampling - Resolution of metrics is set to 5s by default. +- Support for using Kube client auth. +- Improved Influxdb sink - sequence numbers are generated for every metric. +- Reliability improvements +- Bug fixes. + +## 0.9 (3-13-2015) +- [Standardized metrics](sinks/api/supported_metrics.go) +- New [common API](sinks/api/types.go) in place for all external storage drivers. +- Simplified heapster deployment scripts. +- Bug fixes and misc enhancements. + +## 0.8 (2-22-2015) +- Avoid expecting HostIP of Pod to match node's HostIP. + +## 0.7 (2-18-2015) +- Support for Google Cloud Monitoring Backend +- Watch kubernetes api-server instead of polling for pods and nodes info. +- Fetch stats in parallel. +- Refactor code and improve testing. +- Miscellaneous bug fixes. +- Native support for CoreOS. + +## 0.6 (1-21-2015) +- New /validate REST endpoint to probe heapster. +- Heapster supports kube namespaces. +- Heapster uses InfluxDB service DNS name while running in kube mode. + +## 0.5 (12-11-2014) +- Compatiblity with updated InfluxDB service names. + +## 0.4 (12-02-2014) +- Compatibility with cAdvisor v0.6.2 + +## 0.3 (11-26-2014) +- Handle updated Pod API in Kubernetes. + +## 0.2 (10-06-2014) +- Use kubernetes master readonly service which does not require auth + +## 0.1 (10-05-2014) +- First version of heapster. +- Native support for kubernetes and CoreOS. +- For Kubernetes gets pods and rootcgroup information. +- For CoreOS gets containers and rootcgroup information. +- Supports InfluxDB and bigquery. +- Exports pods and container stats in table 'stats' in InfluxDB +- rootCgroup is exported in table 'machine' in InfluxDB +- Special dashboard for kubernetes. diff --git a/vendor/k8s.io/heapster/common/elasticsearch/elasticsearch.go b/vendor/k8s.io/heapster/common/elasticsearch/elasticsearch.go new file mode 100644 index 0000000000..39092d2616 --- /dev/null +++ b/vendor/k8s.io/heapster/common/elasticsearch/elasticsearch.go @@ -0,0 +1,142 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package elasticsearch + +import ( + "fmt" + "net/url" + "strconv" + "time" + + "github.com/golang/glog" + "github.com/olivere/elastic" + "github.com/pborman/uuid" +) + +const ( + ESIndex = "heapster" +) + +// SaveDataFunc is a pluggable function to enforce limits on the object +type SaveDataFunc func(esClient *elastic.Client, indexName string, typeName string, sinkData interface{}) error + +type ElasticSearchConfig struct { + EsClient *elastic.Client + Index string +} + +// SaveDataIntoES save metrics and events to ES by using ES client +func SaveDataIntoES(esClient *elastic.Client, indexName string, typeName string, sinkData interface{}) error { + if indexName == "" || typeName == "" || sinkData == nil { + return nil + } + // Use the IndexExists service to check if a specified index exists. + exists, err := esClient.IndexExists(indexName).Do() + if err != nil { + return err + } + if !exists { + // Create a new index. + createIndex, err := esClient.CreateIndex(indexName).Do() + if err != nil { + return err + } + if !createIndex.Acknowledged { + return fmt.Errorf("failed to create Index in ES cluster: %s", err) + } + } + indexID := uuid.NewUUID() + _, err = esClient.Index(). + Index(indexName). + Type(typeName). + Id(string(indexID)). + BodyJson(sinkData). + Do() + if err != nil { + return err + } + return nil +} + +// CreateElasticSearchConfig creates an ElasticSearch configuration struct +// which contains an ElasticSearch client for later use +func CreateElasticSearchConfig(uri *url.URL) (*ElasticSearchConfig, error) { + + var esConfig ElasticSearchConfig + opts, err := url.ParseQuery(uri.RawQuery) + if err != nil { + return nil, fmt.Errorf("failed to parser url's query string: %s", err) + } + + // set the index for es,the default value is "heapster" + esConfig.Index = ESIndex + if len(opts["index"]) > 0 { + esConfig.Index = opts["index"][0] + } + + // Set the URL endpoints of the ES's nodes. Notice that when sniffing is + // enabled, these URLs are used to initially sniff the cluster on startup. + if len(opts["nodes"]) < 1 { + return nil, fmt.Errorf("There is no node assigned for connecting ES cluster") + } + + startupFns := []elastic.ClientOptionFunc{elastic.SetURL(opts["nodes"]...)} + + // If the ES cluster needs authentication, the username and secret + // should be set in sink config.Else, set the Authenticate flag to false + if len(opts["esUserName"]) > 0 && len(opts["esUserSecret"]) > 0 { + startupFns = append(startupFns, elastic.SetBasicAuth(opts["esUserName"][0], opts["esUserSecret"][0])) + } + + if len(opts["maxRetries"]) > 0 { + maxRetries, err := strconv.Atoi(opts["maxRetries"][0]) + if err != nil { + return nil, fmt.Errorf("Failed to parse URL's maxRetries value into an int") + } + startupFns = append(startupFns, elastic.SetMaxRetries(maxRetries)) + } + + if len(opts["healthCheck"]) > 0 { + healthCheck, err := strconv.ParseBool(opts["healthCheck"][0]) + if err != nil { + return nil, fmt.Errorf("Failed to parse URL's healthCheck value into a bool") + } + startupFns = append(startupFns, elastic.SetHealthcheck(healthCheck)) + } + + if len(opts["sniff"]) > 0 { + sniff, err := strconv.ParseBool(opts["sniff"][0]) + if err != nil { + return nil, fmt.Errorf("Failed to parse URL's sniff value into a bool") + } + startupFns = append(startupFns, elastic.SetSniff(sniff)) + } + + if len(opts["startupHealthcheckTimeout"]) > 0 { + timeout, err := time.ParseDuration(opts["startupHealthcheckTimeout"][0] + "s") + if err != nil { + return nil, fmt.Errorf("Failed to parse URL's startupHealthcheckTimeout: %s", err.Error()) + } + startupFns = append(startupFns, elastic.SetHealthcheckTimeoutStartup(timeout)) + } + + esConfig.EsClient, err = elastic.NewClient(startupFns...) + if err != nil { + return nil, fmt.Errorf("failed to create ElasticSearch client: %v", err) + } + + glog.V(2).Infof("elasticsearch sink configure successfully") + + return &esConfig, nil +} diff --git a/vendor/k8s.io/heapster/common/elasticsearch/elasticsearch_test.go b/vendor/k8s.io/heapster/common/elasticsearch/elasticsearch_test.go new file mode 100644 index 0000000000..7d437a6b73 --- /dev/null +++ b/vendor/k8s.io/heapster/common/elasticsearch/elasticsearch_test.go @@ -0,0 +1,69 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package elasticsearch + +import ( + "net/url" + "reflect" + "testing" + "time" + + "github.com/olivere/elastic" +) + +func TestCreateElasticSearchConfig(t *testing.T) { + url, err := url.Parse("?nodes=https://foo.com:20468&nodes=https://bar.com:20468&esUserName=test&esUserSecret=password&maxRetries=10&startupHealthcheckTimeout=30&sniff=false&healthCheck=false") + if err != nil { + t.Fatalf("Error when parsing URL: %s", err.Error()) + } + + config, err := CreateElasticSearchConfig(url) + if err != nil { + t.Fatalf("Error when creating config: %s", err.Error()) + } + + expectedClient, err := elastic.NewClient( + elastic.SetURL("https://foo.com:20468", "https://bar.com:20468"), + elastic.SetBasicAuth("test", "password"), + elastic.SetMaxRetries(10), + elastic.SetHealthcheckTimeoutStartup(30*time.Second), + elastic.SetSniff(false), elastic.SetHealthcheck(false)) + + if err != nil { + t.Fatalf("Error when creating client: %s", err.Error()) + } + + actualClientRefl := reflect.ValueOf(config.EsClient).Elem() + expectedClientRefl := reflect.ValueOf(expectedClient).Elem() + + if actualClientRefl.FieldByName("basicAuthUsername").String() != expectedClientRefl.FieldByName("basicAuthUsername").String() { + t.Fatalf("basicAuthUsername is not equal") + } + if actualClientRefl.FieldByName("basicAuthUsername").String() != expectedClientRefl.FieldByName("basicAuthUsername").String() { + t.Fatalf("basicAuthUsername is not equal") + } + if actualClientRefl.FieldByName("maxRetries").Int() != expectedClientRefl.FieldByName("maxRetries").Int() { + t.Fatalf("maxRetries is not equal") + } + if actualClientRefl.FieldByName("healthcheckTimeoutStartup").Int() != expectedClientRefl.FieldByName("healthcheckTimeoutStartup").Int() { + t.Fatalf("healthcheckTimeoutStartup is not equal") + } + if actualClientRefl.FieldByName("snifferEnabled").Bool() != expectedClientRefl.FieldByName("snifferEnabled").Bool() { + t.Fatalf("snifferEnabled is not equal") + } + if actualClientRefl.FieldByName("healthcheckEnabled").Bool() != expectedClientRefl.FieldByName("healthcheckEnabled").Bool() { + t.Fatalf("healthcheckEnabled is not equal") + } +} diff --git a/vendor/k8s.io/heapster/common/flags/flags.go b/vendor/k8s.io/heapster/common/flags/flags.go new file mode 100644 index 0000000000..44dc3c2af5 --- /dev/null +++ b/vendor/k8s.io/heapster/common/flags/flags.go @@ -0,0 +1,77 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package flags + +import ( + "bytes" + "fmt" + "net/url" + "os" + "strings" +) + +type Uri struct { + Key string + Val url.URL +} + +func (u *Uri) String() string { + val := u.Val.String() + if val == "" { + return fmt.Sprintf("%s", u.Key) + } + return fmt.Sprintf("%s:%s", u.Key, val) +} + +func (u *Uri) Set(value string) error { + s := strings.SplitN(value, ":", 2) + if s[0] == "" { + return fmt.Errorf("missing uri key in '%s'", value) + } + u.Key = s[0] + if len(s) > 1 && s[1] != "" { + e := os.ExpandEnv(s[1]) + uri, err := url.Parse(e) + if err != nil { + return err + } + u.Val = *uri + } + return nil +} + +type Uris []Uri + +func (us *Uris) String() string { + var b bytes.Buffer + b.WriteString("[") + for i, u := range *us { + if i > 0 { + b.WriteString(" ") + } + b.WriteString(u.String()) + } + b.WriteString("]") + return b.String() +} + +func (us *Uris) Set(value string) error { + var u Uri + if err := u.Set(value); err != nil { + return err + } + *us = append(*us, u) + return nil +} diff --git a/vendor/k8s.io/heapster/common/flags/flags_test.go b/vendor/k8s.io/heapster/common/flags/flags_test.go new file mode 100644 index 0000000000..48eea4ce58 --- /dev/null +++ b/vendor/k8s.io/heapster/common/flags/flags_test.go @@ -0,0 +1,176 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package flags + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUriString(t *testing.T) { + tests := [...]struct { + in Uri + want string + }{ + { + Uri{ + Key: "gcm", + }, + "gcm", + }, + { + Uri{ + Key: "influxdb", + Val: url.URL{ + Scheme: "http", + Host: "monitoring-influxdb:8086", + RawQuery: "key=val&key2=val2", + }, + }, + "influxdb:http://monitoring-influxdb:8086?key=val&key2=val2", + }, + } + for _, c := range tests { + assert.Equal(t, c.want, c.in.String()) + } +} + +func TestUriSet(t *testing.T) { + tests := [...]struct { + in string + want Uri + wantErr bool + }{ + {"", Uri{}, true}, + {":", Uri{}, true}, + {":foo", Uri{}, true}, + {"key:incorrecturl/%gh&%ij", Uri{}, true}, + { + "gcm", + Uri{Key: "gcm"}, + false, + }, + { + "gcm:", + Uri{Key: "gcm"}, + false, + }, + { + "influxdb:http://monitoring-influxdb:8086?key=val&key2=val2", + Uri{ + Key: "influxdb", + Val: url.URL{ + Scheme: "http", + Host: "monitoring-influxdb:8086", + RawQuery: "key=val&key2=val2", + }, + }, + false, + }, + { + "gcm:?metrics=all", + Uri{ + Key: "gcm", + Val: url.URL{ + RawQuery: "metrics=all", + }, + }, + false, + }, + } + for _, c := range tests { + var uri Uri + err := uri.Set(c.in) + if c.wantErr { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, c.want, uri) + } + } +} + +func TestUrisString(t *testing.T) { + tests := [...]struct { + in Uris + want string + }{ + { + Uris{ + Uri{Key: "gcm"}, + }, + "[gcm]", + }, + { + Uris{ + Uri{Key: "gcm"}, + Uri{ + Key: "influxdb", + Val: url.URL{Path: "foo"}, + }, + }, + "[gcm influxdb:foo]", + }, + } + for _, c := range tests { + assert.Equal(t, c.want, c.in.String()) + } +} + +func TestUrisSet(t *testing.T) { + tests := [...]struct { + in []string + want Uris + wantErr bool + }{ + {[]string{""}, Uris{}, true}, + {[]string{":foo"}, Uris{}, true}, + { + []string{"gcm"}, + Uris{ + Uri{Key: "gcm"}, + }, + false, + }, + { + []string{"gcm", "influxdb:foo"}, + Uris{ + Uri{Key: "gcm"}, + Uri{ + Key: "influxdb", + Val: url.URL{Path: "foo"}, + }, + }, + false, + }, + } + for _, c := range tests { + var uris Uris + var err error + for _, s := range c.in { + if err = uris.Set(s); err != nil { + break + } + } + if c.wantErr { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, c.want, uris) + } + } +} diff --git a/vendor/k8s.io/heapster/common/gce/gce.go b/vendor/k8s.io/heapster/common/gce/gce.go new file mode 100644 index 0000000000..f970e373ac --- /dev/null +++ b/vendor/k8s.io/heapster/common/gce/gce.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gce + +import ( + "fmt" + "time" + + "github.com/golang/glog" + "google.golang.org/cloud/compute/metadata" +) + +const ( + waitForGCEInterval = 5 * time.Second + waitForGCETimeout = 3 * time.Minute +) + +func EnsureOnGCE() error { + for start := time.Now(); time.Since(start) < waitForGCETimeout; time.Sleep(waitForGCEInterval) { + glog.Infof("Waiting for GCE metadata to be available") + if metadata.OnGCE() { + return nil + } + } + return fmt.Errorf("not running on GCE") +} diff --git a/vendor/k8s.io/heapster/common/influxdb/dummy_influxdb.go b/vendor/k8s.io/heapster/common/influxdb/dummy_influxdb.go new file mode 100644 index 0000000000..921861d7a7 --- /dev/null +++ b/vendor/k8s.io/heapster/common/influxdb/dummy_influxdb.go @@ -0,0 +1,64 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "strings" + "time" + + influxdb "github.com/influxdata/influxdb/client" +) + +type PointSavedToInfluxdb struct { + Ponit influxdb.Point +} + +type FakeInfluxDBClient struct { + Pnts []PointSavedToInfluxdb +} + +func NewFakeInfluxDBClient() *FakeInfluxDBClient { + return &FakeInfluxDBClient{[]PointSavedToInfluxdb{}} +} + +func (client *FakeInfluxDBClient) Write(bps influxdb.BatchPoints) (*influxdb.Response, error) { + for _, pnt := range bps.Points { + client.Pnts = append(client.Pnts, PointSavedToInfluxdb{pnt}) + } + return nil, nil +} + +func (client *FakeInfluxDBClient) Query(q influxdb.Query) (*influxdb.Response, error) { + numQueries := strings.Count(q.Command, ";") + + // return an empty result for each separate query + return &influxdb.Response{ + Results: make([]influxdb.Result, numQueries), + }, nil +} + +func (client *FakeInfluxDBClient) Ping() (time.Duration, string, error) { + return 0, "", nil +} + +var Client = NewFakeInfluxDBClient() + +var Config = InfluxdbConfig{ + User: "root", + Password: "root", + Host: "localhost:8086", + DbName: "k8s", + Secure: false, +} diff --git a/vendor/k8s.io/heapster/common/influxdb/influxdb.go b/vendor/k8s.io/heapster/common/influxdb/influxdb.go new file mode 100644 index 0000000000..aca2aa2063 --- /dev/null +++ b/vendor/k8s.io/heapster/common/influxdb/influxdb.go @@ -0,0 +1,109 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "fmt" + "net/url" + "strconv" + "time" + + "k8s.io/heapster/version" + + influxdb "github.com/influxdata/influxdb/client" +) + +type InfluxdbClient interface { + Write(influxdb.BatchPoints) (*influxdb.Response, error) + Query(influxdb.Query) (*influxdb.Response, error) + Ping() (time.Duration, string, error) +} + +type InfluxdbConfig struct { + User string + Password string + Secure bool + Host string + DbName string + WithFields bool +} + +func NewClient(c InfluxdbConfig) (InfluxdbClient, error) { + url := &url.URL{ + Scheme: "http", + Host: c.Host, + } + if c.Secure { + url.Scheme = "https" + } + + iConfig := &influxdb.Config{ + URL: *url, + Username: c.User, + Password: c.Password, + UserAgent: fmt.Sprintf("%v/%v", "heapster", version.HeapsterVersion), + } + client, err := influxdb.NewClient(*iConfig) + + if err != nil { + return nil, err + } + if _, _, err := client.Ping(); err != nil { + return nil, fmt.Errorf("failed to ping InfluxDB server at %q - %v", c.Host, err) + } + return client, nil +} + +func BuildConfig(uri *url.URL) (*InfluxdbConfig, error) { + config := InfluxdbConfig{ + User: "root", + Password: "root", + Host: "localhost:8086", + DbName: "k8s", + Secure: false, + WithFields: false, + } + + if len(uri.Host) > 0 { + config.Host = uri.Host + } + opts := uri.Query() + if len(opts["user"]) >= 1 { + config.User = opts["user"][0] + } + // TODO: use more secure way to pass the password. + if len(opts["pw"]) >= 1 { + config.Password = opts["pw"][0] + } + if len(opts["db"]) >= 1 { + config.DbName = opts["db"][0] + } + if len(opts["withfields"]) >= 1 { + val, err := strconv.ParseBool(opts["withfields"][0]) + if err != nil { + return nil, fmt.Errorf("failed to parse `withfields` flag - %v", err) + } + config.WithFields = val + } + if len(opts["secure"]) >= 1 { + val, err := strconv.ParseBool(opts["secure"][0]) + if err != nil { + return nil, fmt.Errorf("failed to parse `secure` flag - %v", err) + } + config.Secure = val + } + + return &config, nil +} diff --git a/vendor/k8s.io/heapster/common/kubernetes/configs.go b/vendor/k8s.io/heapster/common/kubernetes/configs.go new file mode 100644 index 0000000000..d612ea71a0 --- /dev/null +++ b/vendor/k8s.io/heapster/common/kubernetes/configs.go @@ -0,0 +1,140 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubernetes + +import ( + "fmt" + "io/ioutil" + "net/url" + "strconv" + + "k8s.io/kubernetes/pkg/api/unversioned" + kube_client "k8s.io/kubernetes/pkg/client/restclient" + kubeClientCmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + kubeClientCmdApi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" +) + +const ( + APIVersion = "v1" + + defaultKubeletPort = 10255 + defaultKubeletHttps = false + defaultUseServiceAccount = false + defaultServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" + defaultInClusterConfig = true +) + +func getConfigOverrides(uri *url.URL) (*kubeClientCmd.ConfigOverrides, error) { + kubeConfigOverride := kubeClientCmd.ConfigOverrides{ + ClusterInfo: kubeClientCmdApi.Cluster{ + APIVersion: APIVersion, + }, + } + if len(uri.Scheme) != 0 && len(uri.Host) != 0 { + kubeConfigOverride.ClusterInfo.Server = fmt.Sprintf("%s://%s", uri.Scheme, uri.Host) + } + + opts := uri.Query() + + if len(opts["apiVersion"]) >= 1 { + kubeConfigOverride.ClusterInfo.APIVersion = opts["apiVersion"][0] + } + + if len(opts["insecure"]) > 0 { + insecure, err := strconv.ParseBool(opts["insecure"][0]) + if err != nil { + return nil, err + } + kubeConfigOverride.ClusterInfo.InsecureSkipTLSVerify = insecure + } + + return &kubeConfigOverride, nil +} + +func GetKubeClientConfig(uri *url.URL) (*kube_client.Config, error) { + var ( + kubeConfig *kube_client.Config + err error + ) + + opts := uri.Query() + configOverrides, err := getConfigOverrides(uri) + if err != nil { + return nil, err + } + + inClusterConfig := defaultInClusterConfig + if len(opts["inClusterConfig"]) > 0 { + inClusterConfig, err = strconv.ParseBool(opts["inClusterConfig"][0]) + if err != nil { + return nil, err + } + } + + if inClusterConfig { + kubeConfig, err = kube_client.InClusterConfig() + if err != nil { + return nil, err + } + + if configOverrides.ClusterInfo.Server != "" { + kubeConfig.Host = configOverrides.ClusterInfo.Server + } + kubeConfig.GroupVersion = &unversioned.GroupVersion{Version: configOverrides.ClusterInfo.APIVersion} + kubeConfig.Insecure = configOverrides.ClusterInfo.InsecureSkipTLSVerify + if configOverrides.ClusterInfo.InsecureSkipTLSVerify { + kubeConfig.TLSClientConfig.CAFile = "" + } + } else { + authFile := "" + if len(opts["auth"]) > 0 { + authFile = opts["auth"][0] + } + + if authFile != "" { + if kubeConfig, err = kubeClientCmd.NewNonInteractiveDeferredLoadingClientConfig( + &kubeClientCmd.ClientConfigLoadingRules{ExplicitPath: authFile}, + configOverrides).ClientConfig(); err != nil { + return nil, err + } + } else { + kubeConfig = &kube_client.Config{ + Host: configOverrides.ClusterInfo.Server, + Insecure: configOverrides.ClusterInfo.InsecureSkipTLSVerify, + } + kubeConfig.GroupVersion = &unversioned.GroupVersion{Version: configOverrides.ClusterInfo.APIVersion} + } + } + if len(kubeConfig.Host) == 0 { + return nil, fmt.Errorf("invalid kubernetes master url specified") + } + + useServiceAccount := defaultUseServiceAccount + if len(opts["useServiceAccount"]) >= 1 { + useServiceAccount, err = strconv.ParseBool(opts["useServiceAccount"][0]) + if err != nil { + return nil, err + } + } + + if useServiceAccount { + // If a readable service account token exists, then use it + if contents, err := ioutil.ReadFile(defaultServiceAccountFile); err == nil { + kubeConfig.BearerToken = string(contents) + } + } + + return kubeConfig, nil +} diff --git a/vendor/k8s.io/heapster/deploy/docker-compose/README.md b/vendor/k8s.io/heapster/deploy/docker-compose/README.md new file mode 100644 index 0000000000..b25fef6a02 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/docker-compose/README.md @@ -0,0 +1,15 @@ +# Quick start with docker-compose + +Make sure you have installed [Docker Compose](https://docs.docker.com/compose/). Once compose is installed go ahead and fire the awesome: + + $ docker-compose up + +InfluxDB web UI is on http://localhost:8086 and Grafana is available on http://localhost:3000. *Note*: if you are using [boot2docker](https://github.com/boot2docker/boot2docker) you need to replace localhost by the one provided by boot2docker, example: + + $ boot2docker ip + 192.168.59.103 + $ + +in this case you will need to visit http://192.168.59.103:8086 (InfluxDB) and http://192.168.59.103:3000 (Grafana) + +The provided compose it's configured by default with the cAdvisor source and will setup to poll metrics from the `cAdvisor` host defined in sample-hosts.json. diff --git a/vendor/k8s.io/heapster/deploy/docker-compose/docker-compose.yml b/vendor/k8s.io/heapster/deploy/docker-compose/docker-compose.yml new file mode 100644 index 0000000000..337f1929ea --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/docker-compose/docker-compose.yml @@ -0,0 +1,28 @@ +heapster: + image: kubernetes/heapster:v0.14.3 + ports: + - "8082:8082" + links: + - influxdb + - cadvisor + command: --source="cadvisor:external?cadvisorPort=8080" --sink=influxdb:http://influxdb:8086 --vmodule=*=4 + volumes: + - ./sample-hosts.json:/var/run/heapster/hosts +influxdb: + image: tutum/influxdb + ports: + - "8083:8083" + - "8086:8086" +grafana: + image: grafana/grafana + ports: + - "3000:3000" +cadvisor: + image: google/cadvisor:latest + ports: + - "8080:8080" + volumes: + - /:/rootfs:ro + - /var/run:/var/run:rw + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro diff --git a/vendor/k8s.io/heapster/deploy/docker-compose/sample-hosts.json b/vendor/k8s.io/heapster/deploy/docker-compose/sample-hosts.json new file mode 100644 index 0000000000..a7cf663244 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/docker-compose/sample-hosts.json @@ -0,0 +1,8 @@ +{ + "Items": [ + { + "Name": "cAdvisor", + "IP": "cAdvisor" + } + ] +} diff --git a/vendor/k8s.io/heapster/deploy/docker/Dockerfile b/vendor/k8s.io/heapster/deploy/docker/Dockerfile new file mode 100644 index 0000000000..ff32b834bb --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/docker/Dockerfile @@ -0,0 +1,18 @@ +FROM alpine:3.2 +MAINTAINER vishnuk@google.com + +ENV GLIBC_VERSION "2.23-r1" + +RUN apk add --update ca-certificates curl && \ + curl -Ls https://github.com/andyshinn/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk -o glibc-${GLIBC_VERSION}.apk && \ + curl -Ls https://github.com/andyshinn/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-bin-${GLIBC_VERSION}.apk -o glibc-bin-${GLIBC_VERSION}.apk && \ + apk add --allow-untrusted glibc-${GLIBC_VERSION}.apk glibc-bin-${GLIBC_VERSION}.apk + +RUN for cert in `ls -1 /etc/ssl/certs/*.crt | grep -v /etc/ssl/certs/ca-certificates.crt`; do cat "$cert" >> /etc/ssl/certs/ca-certificates.crt; done + +# cAdvisor discovery via external files. +VOLUME /var/run/heapster/hosts +ADD heapster /heapster +ADD eventer /eventer + +ENTRYPOINT ["/heapster"] diff --git a/vendor/k8s.io/heapster/deploy/docker/build.sh b/vendor/k8s.io/heapster/deploy/docker/build.sh new file mode 100755 index 0000000000..cb2452012d --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/docker/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +IMAGE=${1-heapster:canary} + +set -e + +pushd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +godep go build -o heapster -a k8s.io/heapster/metrics +godep go build -o eventer -a k8s.io/heapster/events + +docker build -t $IMAGE . +popd diff --git a/vendor/k8s.io/heapster/deploy/docker/canary/Dockerfile b/vendor/k8s.io/heapster/deploy/docker/canary/Dockerfile new file mode 100644 index 0000000000..ddbcd092a9 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/docker/canary/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.6.0 +MAINTAINER vishnuk@google.com + +RUN apt-get install -y git +RUN git clone https://github.com/kubernetes/heapster.git /go/src/k8s.io/heapster + +RUN cd /go/src/k8s.io/heapster && make && mv heapster /heapster && mv eventer /eventer + +ENTRYPOINT ["/heapster"] diff --git a/vendor/k8s.io/heapster/deploy/kube-config/google/heapster-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/google/heapster-controller.yaml new file mode 100644 index 0000000000..2679e78469 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/google/heapster-controller.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + k8s-app: heapster + name: heapster + version: v6 + name: heapster + namespace: kube-system +spec: + replicas: 1 + selector: + k8s-app: heapster + version: v6 + template: + metadata: + labels: + k8s-app: heapster + version: v6 + spec: + containers: + - name: heapster + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /heapster + - --source=kubernetes:https://kubernetes.default + - --sink=gcm + - --sink=gcl + - --poll_duration=2m + - --stats_resolution=1m + volumeMounts: + - mountPath: /etc/ssl/certs + name: ssl-certs + readOnly: true + volumes: + - name: ssl-certs + hostPath: + path: /etc/ssl/certs diff --git a/vendor/k8s.io/heapster/deploy/kube-config/google/heapster-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/google/heapster-service.yaml new file mode 100644 index 0000000000..4184009896 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/google/heapster-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + kubernetes.io/cluster-service: 'true' + kubernetes.io/name: Heapster + name: heapster + namespace: kube-system +spec: + ports: + - port: 80 + targetPort: 8082 + selector: + k8s-app: heapster diff --git a/vendor/k8s.io/heapster/deploy/kube-config/influxdb/grafana-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/grafana-service.yaml new file mode 100644 index 0000000000..148e88ac74 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/grafana-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + kubernetes.io/cluster-service: 'true' + kubernetes.io/name: monitoring-grafana + name: monitoring-grafana + namespace: kube-system +spec: + # In a production setup, we recommend accessing Grafana through an external Loadbalancer + # or through a public IP. + # type: LoadBalancer + ports: + - port: 80 + targetPort: 3000 + selector: + name: influxGrafana diff --git a/vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-controller.yaml new file mode 100644 index 0000000000..2ca630f159 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-controller.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + k8s-app: heapster + name: heapster + version: v6 + name: heapster + namespace: kube-system +spec: + replicas: 1 + selector: + k8s-app: heapster + version: v6 + template: + metadata: + labels: + k8s-app: heapster + version: v6 + spec: + containers: + - name: heapster + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /heapster + - --source=kubernetes:https://kubernetes.default + - --sink=influxdb:http://monitoring-influxdb:8086 diff --git a/vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-service.yaml new file mode 100644 index 0000000000..4184009896 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/heapster-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + kubernetes.io/cluster-service: 'true' + kubernetes.io/name: Heapster + name: heapster + namespace: kube-system +spec: + ports: + - port: 80 + targetPort: 8082 + selector: + k8s-app: heapster diff --git a/vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-grafana-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-grafana-controller.yaml new file mode 100644 index 0000000000..1e51d088d8 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-grafana-controller.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + name: influxGrafana + name: influxdb-grafana + namespace: kube-system +spec: + replicas: 1 + selector: + name: influxGrafana + template: + metadata: + labels: + name: influxGrafana + spec: + containers: + - name: influxdb + image: kubernetes/heapster_influxdb:v0.5 + volumeMounts: + - mountPath: /data + name: influxdb-storage + - name: grafana + image: gcr.io/google_containers/heapster_grafana:v2.6.0-2 + env: + - name: INFLUXDB_SERVICE_URL + value: http://monitoring-influxdb:8086 + # The following env variables are required to make Grafana accessible via + # the kubernetes api-server proxy. On production clusters, we recommend + # removing these env variables, setup auth for grafana, and expose the grafana + # service using a LoadBalancer or a public IP. + - name: GF_AUTH_BASIC_ENABLED + value: "false" + - name: GF_AUTH_ANONYMOUS_ENABLED + value: "true" + - name: GF_AUTH_ANONYMOUS_ORG_ROLE + value: Admin + - name: GF_SERVER_ROOT_URL + value: /api/v1/proxy/namespaces/kube-system/services/monitoring-grafana/ + volumeMounts: + - mountPath: /var + name: grafana-storage + volumes: + - name: influxdb-storage + emptyDir: {} + - name: grafana-storage + emptyDir: {} diff --git a/vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-service.yaml new file mode 100644 index 0000000000..89c151cb25 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/influxdb/influxdb-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: null + name: monitoring-influxdb + namespace: kube-system +spec: + ports: + - name: http + port: 8083 + targetPort: 8083 + - name: api + port: 8086 + targetPort: 8086 + selector: + name: influxGrafana diff --git a/vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-controller.yaml new file mode 100644 index 0000000000..53ef783e85 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-controller.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + k8s-app: heapster + name: heapster + version: v6 + name: heapster + namespace: kube-system +spec: + replicas: 1 + selector: + k8s-app: heapster + version: v6 + template: + metadata: + labels: + k8s-app: heapster + version: v6 + spec: + containers: + - name: heapster + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /heapster + - --source=kubernetes:https://kubernetes.default + - --sink=kafka:?brokers=monitoring-kafka:9092×eriestopic=timeseries&eventstopic=events diff --git a/vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-service.yaml new file mode 100644 index 0000000000..4184009896 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/kafka/heapster-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + kubernetes.io/cluster-service: 'true' + kubernetes.io/name: Heapster + name: heapster + namespace: kube-system +spec: + ports: + - port: 80 + targetPort: 8082 + selector: + k8s-app: heapster diff --git a/vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-controller.yaml new file mode 100644 index 0000000000..fcefbfbc89 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-controller.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + name: kafka + name: kafka + namespace: kube-system +spec: + replicas: 1 + selector: + name: kafkazookeeper + template: + metadata: + labels: + name: kafkazookeeper + spec: + containers: + - name: kafkazookeeper + image: kubernetes/heapster_kafka:canary + env: + - name: ADVERTISED_PORT + value: '9092' diff --git a/vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-service.yaml new file mode 100644 index 0000000000..7c0fa0ef09 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/kafka/kafka-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: null + name: monitoring-kafka + namespace: kube-system +spec: + ports: + - name: http + port: 9092 + targetPort: 9092 + selector: + name: kafka + diff --git a/vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-controller.json b/vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-controller.json new file mode 100644 index 0000000000..2a3a506f4b --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-controller.json @@ -0,0 +1,49 @@ +{ + "apiVersion": "v1", + "kind": "ReplicationController", + "metadata": { + "labels": { + "name": "riemann-heapster" + }, + "name": "riemann-heapster-controller" + }, + "spec": { + "replicas": 1, + "selector": { + "name": "riemann-heapster" + }, + "template": { + "metadata": { + "labels": { + "name": "riemann-heapster" + } + }, + "spec": { + "containers": [ + { + "image": "kubernetes/heapster_riemann:canary", + "imagePullPolicy": "Always", + "name": "riemann-heapster", + "ports": [ + { + "name": "event-tcp", + "protocol": "TCP", + "containerPort": 5555 + }, + { + "name": "event-udp", + "protocol": "UDP", + "containerPort": 5555 + }, + { + "name": "websockets", + "protocol": "TCP", + "containerPort": 5556 + } + ] + } + ] + } + } + } +} diff --git a/vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-service.json b/vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-service.json new file mode 100644 index 0000000000..c47a0a6ab7 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/riemann/riemann-service.json @@ -0,0 +1,36 @@ +{ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "labels": { + "kubernetes.io/cluster-service": "true", + "name": "riemann-heapster" + }, + "name": "riemann-heapster" + }, + "spec": { + "ports": [ + { + "name": "event-tcp", + "protocol": "TCP", + "port": 5555, + "targetPort": 5555 + }, + { + "name": "event-udp", + "protocol": "UDP", + "port": 5555, + "targetPort": 5555 + }, + { + "name": "websockets", + "protocol": "TCP", + "port": 5556, + "targetPort": 5556 + } + ], + "selector": { + "name": "riemann-heapster" + } + } +} diff --git a/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-controller.yaml new file mode 100644 index 0000000000..d4d9c9ec98 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-controller.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + k8s-app: heapster-test + name: heapster + name: heapster + namespace: heapster-e2e-tests +spec: + replicas: 1 + selector: + k8s-app: heapster-test + template: + metadata: + labels: + k8s-app: heapster-test + spec: + containers: + - name: heapster-test + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /heapster + - --source=kubernetes:https://kubernetes.default + - --sink=log + volumeMounts: + - name: ssl-certs + mountPath: /etc/ssl/certs + readOnly: true + - name: eventer-test + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /eventer + - --source=kubernetes:https://kubernetes.default + - --sink=log + volumeMounts: + - name: ssl-certs + mountPath: /etc/ssl/certs + readOnly: true + volumes: + - name: ssl-certs + hostPath: + path: /etc/ssl/certs diff --git a/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-service.yaml new file mode 100644 index 0000000000..16624b5cde --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: heapster + namespace: heapster-e2e-tests +spec: + ports: + - port: 80 + targetPort: 8082 + selector: + k8s-app: heapster-test diff --git a/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-summary-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-summary-controller.yaml new file mode 100644 index 0000000000..46d5c51536 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/standalone-test/heapster-summary-controller.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + k8s-app: heapster-test + name: heapster + name: heapster + namespace: heapster-e2e-tests +spec: + replicas: 1 + selector: + k8s-app: heapster-test + template: + metadata: + labels: + k8s-app: heapster-test + spec: + containers: + - name: heapster-test + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /heapster + - --source=kubernetes.summary_api:https://kubernetes.default + - --sink=log + volumeMounts: + - name: ssl-certs + mountPath: /etc/ssl/certs + readOnly: true + - name: eventer-test + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /eventer + - --source=kubernetes:https://kubernetes.default + - --sink=log + volumeMounts: + - name: ssl-certs + mountPath: /etc/ssl/certs + readOnly: true + volumes: + - name: ssl-certs + hostPath: + path: /etc/ssl/certs diff --git a/vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-controller.yaml b/vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-controller.yaml new file mode 100644 index 0000000000..d5febc507a --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-controller.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + labels: + k8s-app: heapster + name: heapster + version: v6 + name: heapster + namespace: kube-system +spec: + replicas: 1 + selector: + k8s-app: heapster + version: v6 + template: + metadata: + labels: + k8s-app: heapster + version: v6 + spec: + containers: + - name: heapster + image: kubernetes/heapster:canary + imagePullPolicy: Always + command: + - /heapster + - --source=kubernetes:https://kubernetes.default + volumeMounts: + - name: ssl-certs + mountPath: /etc/ssl/certs + readOnly: true + volumes: + - name: ssl-certs + hostPath: + path: /etc/ssl/certs diff --git a/vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-service.yaml b/vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-service.yaml new file mode 100644 index 0000000000..4184009896 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube-config/standalone/heapster-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + kubernetes.io/cluster-service: 'true' + kubernetes.io/name: Heapster + name: heapster + namespace: kube-system +spec: + ports: + - port: 80 + targetPort: 8082 + selector: + k8s-app: heapster diff --git a/vendor/k8s.io/heapster/deploy/kube.sh b/vendor/k8s.io/heapster/deploy/kube.sh new file mode 100755 index 0000000000..b102d61532 --- /dev/null +++ b/vendor/k8s.io/heapster/deploy/kube.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/kube-config/influxdb" + +start() { + if kubectl.sh create -f "$DIR/" &> /dev/null; then + echo "heapster pods have been setup" + else + echo "failed to setup heapster pods" + fi +} + +stop() { + kubectl.sh stop replicationController monitoring-influx-grafana-controller &> /dev/null + kubectl.sh stop replicationController monitoring-heapster-controller &> /dev/null + # wait for the pods to disappear. + while kubectl.sh get pods -l "name=influxGrafana" -o template -t {{range.items}}{{.id}}:{{end}} | grep -c . &> /dev/null \ + || kubectl.sh get pods -l "name=heapster" -o template -t {{range.items}}{{.id}}:{{end}} | grep -c . &> /dev/null; do + sleep 2 + done + kubectl.sh delete -f "$DIR/" &> /dev/null || true + echo "heapster pods have been removed." +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + stop + start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + ;; +esac + +exit 0 diff --git a/vendor/k8s.io/heapster/docs/debugging.md b/vendor/k8s.io/heapster/docs/debugging.md new file mode 100644 index 0000000000..3a9b8d710b --- /dev/null +++ b/vendor/k8s.io/heapster/docs/debugging.md @@ -0,0 +1,72 @@ +## Heapster Debugging: + +This is a collection of common issues faced by users and ways to debug them. + +Depending on the deployment setup, the issue could be either with Heapster, cAdvisor, Kubernetes, or the monitoring backend. + +### Heapster Core + +#### Common Problems + +* Some distros (including Debian) ship with memory accounting disabled by default. To enable memory and swap accounting on the nodes, follow [these instructions](https://docs.docker.com/installation/ubuntulinux/#memory-and-swap-accounting). + +#### Debuging + +There are 2 endpoints that can give you an insight into what is going on in Heapster: + +* `/metrics` contains lots of metrics in Prometheus format that can indicate the root cause of Heapster problems. Example: +``` +master:~$ curl 10.244.1.3:8082/metrics +# HELP heapster_exporter_duration_microseconds Time spent exporting data to sink in microseconds. +# TYPE heapster_exporter_duration_microseconds summary +heapster_exporter_duration_microseconds{exporter="InfluxDB Sink",quantile="0.5"} 3.497 +heapster_exporter_duration_microseconds{exporter="InfluxDB Sink",quantile="0.9"} 5.296 +heapster_exporter_duration_microseconds{exporter="InfluxDB Sink",quantile="0.99"} 5.296 +heapster_exporter_duration_microseconds_sum{exporter="InfluxDB Sink"} 16698.508000000013 +heapster_exporter_duration_microseconds_count{exporter="InfluxDB Sink"} 3089 +heapster_exporter_duration_microseconds{exporter="Metric Sink",quantile="0.5"} 4.546 +heapster_exporter_duration_microseconds{exporter="Metric Sink",quantile="0.9"} 7.632 +heapster_exporter_duration_microseconds{exporter="Metric Sink",quantile="0.99"} 7.632 +heapster_exporter_duration_microseconds_sum{exporter="Metric Sink"} 25597.190999999973 +heapster_exporter_duration_microseconds_count{exporter="Metric Sink"} 3089 +[...] +``` +This endpoint is enabled for both metrics(Heapster) and events(Eventer). + + +* `/api/v1/model/debug/allkeys` has a list of all metrics sets that are processed inside Heapster. This can be usefull to check what is +passed to your configured sinks Example: + +``` +master:~$ curl 10.244.1.3:8082/api/v1/model/debug/allkeys +[ + "namespace:kube-system/pod:kube-dns-v10-qey9d", + "namespace:default/pod:resource-consumer-qcnzr", + "namespace:default", + "cluster", + "node:kubernetes-minion-fpdd/container:kubelet", + "namespace:kube-system/pod:kube-proxy-kubernetes-minion-fpdd/container:kube-proxy", + "node:kubernetes-minion-j82g/container:system", + "namespace:kube-system/pod:kube-proxy-kubernetes-minion-j82g/container:kube-proxy", + "node:kubernetes-minion-j82g/container:docker-daemon", + "namespace:kube-system/pod:monitoring-influxdb-grafana-v3-q3ozn/container:grafana", + "namespace:kube-system/pod:kubernetes-dashboard-v1.0.0beta1-085ag", + "node:kubernetes-minion-j82g/container:kubelet", + "namespace:kube-system/pod:kube-dns-v10-qey9d/container:healthz", + "node:kubernetes-minion-fhue", + [...] + ``` +This is enabled for metrics only. + +#### Extra Logging + +Moreover additional logging can be enabled by setting an extra flag `--vmodule=*=4`. +You can also enable a sink that writes all metrics or events to stdout with `--sink=log` added to command line parameters. +Both changes require restarting Heapster though. + +### InfluxDB & Grafana + +Ensure Influxdb is up and reachable. Heapster attempts to create a database by default, which will fail eventually after a fixed number of retries. +If the Grafana queries are stuck or slow, it is due to InfluxDB being unresponsive. Consider providing InfluxDB more compute resources (CPU and Memory). +The default database on Influxdb is 'k8s'. +A `list series` query on 'k8s' database should list all the series being pushed by heapster. If you do not see any series, take a look at heapster logs. diff --git a/vendor/k8s.io/heapster/docs/google.md b/vendor/k8s.io/heapster/docs/google.md new file mode 100644 index 0000000000..9a4ee2541d --- /dev/null +++ b/vendor/k8s.io/heapster/docs/google.md @@ -0,0 +1,24 @@ +## Running Heapster in Kubernetes with a Google Cloud Monitoring and Google Cloud Logging backend + +The Google Cloud Monitoring and Logging backends only works in Google Compute Engine today. + +### Setup a Kubernetes cluster +[Bring up a Kubernetes cluster](https://github.com/kubernetes/kubernetes), if you haven't already. Ensure that `kubecfg.sh` is exported. + +### Start all of the pods and services + +Start the Heapster service on the cluster: + +```shell +$ kubectl.sh create -f deploy/kube-config/google/ +``` + +Heapster metrics are now being exported to Google Cloud Monitoring as custom metrics. Heapster events are now being exported to Google Cloud Logging as custom logs. + +### Metrics Dashboard +To access the Google Cloud Monitoring dashboard go to: [https://app.google.stackdriver.com/](https://app.google.stackdriver.com/). Create a new dashboard and add the desired charts. Select the *Custom Metric* Resource Type and all Heapster metrics are under the `kubernetes.io` namespace. You can narrow down the query by the metric labels provided. + +It is also possible to query the Google Cloud Monitoring data directly using their [custom metric read API](https://cloud.google.com/monitoring/v2beta2/timeseries/list). + +### Events Dashboard +To access events via Google Cloud Logging dashboard go to [Google Developer Console](https://cloud.google.com) and select 'Logs' under 'Monitoring' in your project. In the Logs dashboard, select `custom.googleapis.com` as the logs source. diff --git a/vendor/k8s.io/heapster/docs/hacking.md b/vendor/k8s.io/heapster/docs/hacking.md new file mode 100644 index 0000000000..e129c89323 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/hacking.md @@ -0,0 +1,11 @@ +# Hacking + +### Godep + +We use [go-extpoints](https://github.com/progrium/go-extpoints) to generate +code, which is included via Godeps. When you want to update the dependencies, +make sure to run `godep save ./... github.com/progrium/go-extpoints` instead +of the usual `godep save ./...` to not remove the vendored `go-extpoints`. + +Even if you do, Travis should catch that since `hooks/run-extpoints.sh` +depends on those vendored source files. diff --git a/vendor/k8s.io/heapster/docs/influxdb.md b/vendor/k8s.io/heapster/docs/influxdb.md new file mode 100644 index 0000000000..ed3a965757 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/influxdb.md @@ -0,0 +1,30 @@ +# Run Heapster in a Kubernetes cluster with an InfluxDB backend and a Grafana UI + +### Setup a Kubernetes cluster +[Bring up a Kubernetes cluster](https://github.com/kubernetes/kubernetes), if you haven't already. Ensure that `kubecfg.sh` is exported. + +### Start all of the pods and services +```shell +$ kubectl create -f deploy/kube-config/influxdb/ +``` + +Grafana service by default requests for a LoadBalancer. If that is not available in your cluster, consider changing that to NodePort. Use the external IP assigned to the Grafana service, +to access Grafana. +The default user name and password is 'admin'. +Once you login to Grafana, add a datasource that is InfluxDB. The URL for InfluxDB will be `http://localhost:8086`. Database name is 'k8s'. Default user name and password is 'root'. +Grafana documentation for InfluxDB [here](http://docs.grafana.org/datasources/influxdb/). + +Take a look at the [storage schema](storage-schema.md) to understand how metrics are stored in InfluxDB. + +Grafana is set up to auto-populate nodes and pods using templates. + +## Troubleshooting guide +1. If the Grafana service is not accessible, chances are it might not be running. Use `kubectl.sh` to verify that the `heapster` and `influxdb & grafana` pods are alive. + + kubectl get pods + + kubectl get services + +2. To access the InfluxDB UI, you will have to make the InfluxDB service externally visible, similar to how Grafana is made publicly accessible. + +3. If you find InfluxDB to be using up a lot of CPU or memory, consider placing resource restrictions on the `InfluxDB & Grafana` pod. You can add `cpu: ` and `memory: ` in the [Controller Spec](../deploy/kube-config/influxdb/influxdb-grafana-controller.yaml) and relaunch the controllers: diff --git a/vendor/k8s.io/heapster/docs/integration.md b/vendor/k8s.io/heapster/docs/integration.md new file mode 100644 index 0000000000..c70c2015c4 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/integration.md @@ -0,0 +1,7 @@ +## Heapster integration testing + +Heapster can be tested against an existing Kubernetes cluster running on GCE. Tests under [integration](../integration) can be used for integration testing. + + make test-integration + +Note that the test expects Kube Config to exist under `~/.kube/.kubeconfig`. To override the default path using the flag `--kube_config` diff --git a/vendor/k8s.io/heapster/docs/model.md b/vendor/k8s.io/heapster/docs/model.md new file mode 100644 index 0000000000..8c41939a2c --- /dev/null +++ b/vendor/k8s.io/heapster/docs/model.md @@ -0,0 +1,77 @@ +# Heapster Metric Model + +The Heapster Model is a structured representation of metrics for Kubernetes clusters, which is exposed through a set of REST API endpoints. +It allows the extraction of up to 15 minutes of historical data for any Container, Pod, Node or Namespace in the cluster, as well as the cluster itself (depending on the metric). + +**Please bear in mind that this is not an official Kubernetes API, we will try to keep it stable but we don't guarantee that we won't change it in the future.** + +## Usage + +The Heapster Model is enabled by default. The resolution of the model can be configured through +the `-model_resolution` flag, which will cause the model to store historical data at the specified resolution. If the `-model_resolution` flag is not specified, the default resolution of 30 seconds will be used. + +## API documentation + +A detailed documentation of each API endpoint is listed below. + +All endpoints ending in `/metrics/{metric-name}/` can accept the optional `start` and `end` query parameters +that represent the start and end time of the requested timeseries. The result +will be a list of (Timestamp, Value) pairs in the time range [start, end]. +`start` and `end` are strings formatted according to RFC3339. If `start` is not +defined, it is assumed as the zero Unix epoch time. If `end` is not defined, +then all data later than `start` will be returned. + +### Cluster-level Metrics + +`/api/v1/model/metrics/`: Returns a list of available cluster-level metrics. + +`/api/v1/model/metrics/{metric-name}?start=X&end=Y`: Returns a set of (Timestamp, Value) +pairs for the requested cluster-level metric, between the time range specified by `start` and `end`. + +### Node-level Metrics +`/api/v1/model/nodes/`: Returns a list of all available nodes. + +`/api/v1/model/nodes/{node-name}/metrics/`: Returns a list of available +node-level metrics. + +`/api/v1/model/nodes/{node-name}/metrics/{metric-name}?start=X&end=Y`: Returns a set of (Timestamp, Value) +pairs for the requested node-level metric, within the time range specified by `start` and `end`. + +### Namespace-level Metrics +`/api/v1/model/namespaces/`: Returns a list of all available namespaces. + +`/api/v1/model/namespaces/{namespace-name}/metrics/`: Returns a list of available namespace-level metrics. + +`/api/v1/model/namespaces/{namespace-name}/metrics/{metric-name}?start=X&end=Y`: Returns a set of (Timestamp, Value) +pairs for the requested namespace-level metric, within the time range specified by `start` and `end`. + + +### Pod-level Metrics +`/api/v1/model/namespaces/{namespace-name}/pods/`: Returns a list of all available pods under a given namespace. + +`/api/v1/model/namespaces/{namespace-name}/pods/{pod-name}/metrics/`: Returns a list of available pod-level metrics + +`/api/v1/model/namespaces/{namespace-name}/pods/{pod-name}/metrics/{metric-name}?start=X&end=Y`: Returns a set of (Timestamp, Value) +pairs for the requested pod-level metric, within the time range specified by `start` and `end`. + +### Container-level Metrics +Container metrics and stats are accessible for both containers that belong to +pods, as well as for free containers running in each node. + +`/api/v1/model/namespaces/{namespace-name}/pods/{pod-name}/containers/`: Returns a list of all available containers under a given pod. + +`/api/v1/model/namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics/`: Returns a list of available container-level metrics + +`/api/v1/model/namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics/{metric-name}?start=X&end=Y`: Returns a set of (Timestamp, Value) +pairs for the requested container-level metric, within the time range specified by `start` and `end`. + +`/api/v1/model/nodes/{node-name}/freecontainers/`: Returns a list of all available free containers under a given node. + +`/api/v1/model/nodes/{node-name}/freecontainers/{container-name}/metrics/`: Returns a list of available container-level metrics + +`/api/v1/model/nodes/{node-name}/freecontainers/{container-name}/metrics/{metric-name}?start=X&end=Y`: Returns a set of (Timestamp, Value) +pairs for the requested container-level metric, within the time range specified by `start` and `end`. + +### Metric Types + +All metrics available in the [storage schema](storage-schema.md) are also available through the api. diff --git a/vendor/k8s.io/heapster/docs/overview.md b/vendor/k8s.io/heapster/docs/overview.md new file mode 100644 index 0000000000..9887b45082 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/overview.md @@ -0,0 +1,12 @@ +Heapster Overview +=================== + +Heapster is a monitoring merics and events processing tool designed to work inside Kubernetes clusters. It consists of 2 components: + +* Heapster core that reads [metrics](storage-schema.md) from Kubernetes cluster nodes (see [sources](source-configuration.md)), +do some processing and writes them to permanent storage (see [sinks](sink-configuration.md)). +It also provides metrics for other Kubernetes components through [Model API](model.md). + +* Eventer that reads events from Kubernetes master (see [sources](source-configuration.md)) and writes them to permanent storage +(see [sinks](sink-configuration.md)). + diff --git a/vendor/k8s.io/heapster/docs/proposals/old-timer.md b/vendor/k8s.io/heapster/docs/proposals/old-timer.md new file mode 100644 index 0000000000..c43c73ae60 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/proposals/old-timer.md @@ -0,0 +1,228 @@ +# Heapster Oldtimer + +## Overview + +Prior to the Heapster refactor, the Heapster model presented aggregations of +metrics over certain time periods (the last hour and day). Post-refactor, the +concern of presenting an interface for historical metrics was to be split into +a separate Heapster component: Oldtimer. + +Oldtimer will run as part of the main Heapster executable, and will present +common interfaces for retrieving historical metrics over longer periods of time +than the Heapster model, and will allow fetching aggregations of metrics (e.g. +averages, 95 percentile, etc) over different periods of time. It will do this +by querying the sink to which it is storing metrics. + +Note: even though we are retrieving metrics, this document refers to the +metrics storage locations as "sinks" to be consistent with the rest +of Heapster. + +## Motivation + +There are two major motivations for exposing historical metrics information: + +1. Using aggregated historical data to make size-related decisions + (for example, idling requires looking for traffic over a long time period) + +2. Providing a common interface for users to view historical metrics + +Before the Heapster refactoring (see the +[Heapster Long Term Vision Proposal](https://github.com/kubernetes/heapster/blob/master/docs/proposals/vision.md)), +Heapster supported querying metrics aggregated over certain extended time +periods (the last hour and day) via the Heapster model. + +However, since the Heapster model is stored in-memory, and not persisted to +disk, this historical data would be "lost" whenever Heapster was restarted. +This made it unreliable for use by system components which need a historical +view. + +Since we already persist metrics into a sink, it does not make sense for +Heapster itself to persist long-term metrics to disk itself. Instead, we can +just query the sink directly. + +## API + +Oldtimer will present an api somewhat similar to the normal Heapster model. +The structure of the URLs is designed to mirror those exposed by the model API. +When used simply to retrieve historical data points, Oldtimer will return the +same types as the model API. When the used to retrieve aggregations, Oldtimer +will return special data types detailed under the "Return Types" section. + +### Paths + +`/api/v1/historical/{prefix}/metrics/`: Returns a list of all available +metrics. + +`/api/v1/historical{prefix}/metrics/{metric-name}?start=X&end=Y`: Returns a set +of (Timestamp, Value) pairs for the requested {prefix}-level metric, over the +given time range. + +`/api/v1/historical{prefix}/metrics-aggregated/{aggregations}/{metric-name}?start=X&end=Y&bucket=B` +Returns the requested {prefix}-level metric, aggregated with the given +aggregation over the requested time period (potentially split into several +different bucket of duration `B`). `{aggregations}` may be a comma-separated +list of aggregations to retrieve multiple at once. + +Where `{prefix}` is normally either empty (cluster-level), +`/namespaces/{namespace}` (namespace-level), +`/namespaces/{namespace}/pods/{pod-name}` (pod-level), +`/namespaces/{namespace}/pod-list/{pod-list}` (multi-pod-level), or +`/namespaces/{namespace}/pods/{pod-name}/containers/{container-name}` +(container-level). + +Additionally, since pod names are not temporally unique (i.e. it is possible to +delete a pod, and then create a new, completely different pod with the same +name), `{prefix}` may also be `/pod-id/{pod-id}` (pod-level metrics), +`/pod-id-list/{pod-id-list}` (multi-pod-level), or +`/pod-id/{pod-id}/containers/{container-name}` (container-level metrics). + +In addition, when `{prefix}` is not empty, there will be a url of the form: +`/api/v1/historical/{prefix-without-final-element}` which allows fetching the +list of available nodes/namespaces/pods/containers. + +Note that queries by pod name will return metrics from the latest pod with the +given name. This may require an extra trip to the database in some cases, in +order to determine which pod id that actually is. For this reason, if a +component knows the pod ids for which it is querying, using these is preferred +to using the pod names. The pod-name-based API is retained for the sake of +easy queries and to match up with the model API. + +### Parameter Types + +The `start` and `end` parameters are defined the same way as for the model: +each should be a timestamp formatted according to RFC 3339, if no start time is +specified, it defaults to zero in Unix epoch time, and if no end time is +specified, all data after the start time will be considered. + +The `bucket` (bucket duration) parameter is a number followed by any of the +following suffixes: + +- `ms`: milliseconds +- `s`: seconds +- `m`: minutes +- `h`: hours +- `d`: days + +### Return Types + +For requests which simply fetch data points or list available objects, the +return format will be the same as that used in the Heapster model API. + +The the case of aggregations, a different set of types is used: each bucket is +represented by a `MetricAggregationBucket`, which contains the timestamp for +that bucket (the start of the bucket), the count of entries in that bucket (if +requested) as an unsigned integer, as well as each of the other requested +aggregations, in the form of a `MetricValue` (which just holds an unsigned int +or a float). + +All buckets for a particular metric are grouped together in a +`MetricAggregationResult`, which also holds the bucket size (duration) for the +buckets. If multiple pods are requested, the result will be returned as a +`MetricAggregationResultList`, similarly to the `MetricResultList` for the +model API. + +```go +type MetricValue struct { + IntValue *uint64 + FloatValue *float64 +} + +type MetricAggregationBucket struct { + Timestamp time.Time + Count *uint64 + + Average *MetricValue + Maximum *MetricValue + Minimum *MetricValue + Median *MetricValue + Percentiles map[uint64]MetricValue +} + +type MetricAggregationResult struct { + Buckets []MetricAggregationBucket + BucketSize time.Duration +} + +type MetricAggregationResultList struct { + Items []MetricAggregationResult +} +``` + +### Aggregations + +Several different aggregations will be supported. Aggregations should be +performed in the metrics sink. If more aggregations later become supported +across all metrics sinks, the list can be expanded. + +- Average (arithmetic mean): `/metrics-aggregated/average` +- Maximum: `/metrics-aggregated/max` +- Minimum: `/metrics-aggregated/min` +- Percentile: `/metrics-aggregated/{number}-perc` +- Median: `/metrics-aggregated/median` +- Count: `/metrics-aggregated/count` + +Note: to support all the existing sinks, the supported percentiles will be +limitted to 50, 95, and 99. If additional percentile values later become +supported by other sinks, this list may be expanded (see the Sink Support +section below). + +### Example + +Suppose that one wanted to retrieve the 95th percentile of CPU usage for a +given pod over the past 30 days, in 1 hour intervals, along with the maximum +usage for each interval. Call the pod "somepod", in the namespace "somens". +To fetch the results, you'd perform: + +``` +GET /api/v1/historical/namespaces/somens/pods/somepod/metrics-aggregated/95-perc,average/cpu/usage?start=2016-03-20T10:57:37-04:00&bucket=1h +``` + +Which would then return: + +```json +{ + "bucketSize": "3600000000000", + "buckets": [ + { + "timestamp": "2016-03-20T10:57:37-04:00", + "average": "32", + "percentiles": { + "95": "27" + } + }, + ... + ] +} +``` + +## Sink Support and Functionality + +When Oldtimer receives a request, it will compose a query to the sink, send the +query to the sink, and the transform the results into the appropriate API +formats. Note that Oldtimer is designed to retrieve information that was +originally written by Heapster itself. Any information read by Oldtimer must +have been stored according to the Heapster storage schema. + +All computations, filtering, etc should be performed in the sink. Oldtimer +should only be composing queries. Ergo, the feature set of Oldtimer must +represent the lowest-common-denominator of features supported by the sinks. +Oldtimer is meant to be an API for performing basic aggregations supported by +all of the sinks, and is not meant to be a general purpose query tool. + +At the time of writing of this proposal, the following sinks were considered: +Hawkular, InfluxDB, GCM, and OpenTSDB. However, the aggregations supported are +fairly basic, so if new sinks are added, it should be fairly likely that they +support the required Oldtimer features. + +## Scaling and Performance Considerations + +Since Oldtimer itself does not store any data, it should have a fairly low +memory footprint. The current plan is to have Oldtimer run as part of the main +Heapster executable. However, in the future it may be advantageous to have the +ability to split Oldtimer out into a separate executable in order to scale it +independently of Heapster. + +The metrics sinks themselves should already have clustering support, and thus +can be scaled if needed. Since Oldtimer queries the metrics sinks themselves, +response latency should depend mainly on how quickly the sinks can respond to +queries. diff --git a/vendor/k8s.io/heapster/docs/proposals/push-metrics.md b/vendor/k8s.io/heapster/docs/proposals/push-metrics.md new file mode 100644 index 0000000000..d3cceb3752 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/proposals/push-metrics.md @@ -0,0 +1,233 @@ +# Heapster Push Metrics + +## Overview and Motivation + +Currently, Heapster supports pulling metrics from kubelet, and defines an +interface for pulling from other sources. However, in certain cases, it is +more useful to be able to have services push metrics into Heapster, instead +of having Heapster pull metrics. + +For instance, supporting push metrics makes it easy for cluster admins to add +custom metrics into Heapster with relatively minimal effort: they can simply +write a program or script which collects and processes the information, and +then add a recurring Job or cron job which pushes the metrics. This also +enables existing tooling designed with the push model in mind to be adapted to +provide metrics through Heapster. + +### Target Audience and Metrics + +Like the existing custom metrics pull mechanism, the metrics pushed through the +push mechanism are intended to be those metrics that are useful for consumption +by system components, such as metrics intended for use with autoscaling. The +custom metrics pushed through the push mechanisms are still intended to follow +the overall guidelines for Heapster custom metrics (keep the number of +different metric names relatively limited, etc). + +The current custom metrics mechanism can only be used to collect custom metrics +which describe the producing pod. This proposal is designed to provide a +method of collecting metrics which describe multiple resources (other pods, +services, etc) across the cluster. Such producers are generally add-on cluster +infrastructure components deployed by the cluster admins. The push mechanisms +are not, in general, intended for use by arbitrary cluster users (although the +metrics transfered would probably apply to the users' pods). It is up to +cluster admins to decide which applications are permitted to use the push +mechanims (see the "Authentication" section below for more information on how +cluster admins can control access). + +For these reasons, the push metrics mechanism is not designed to replace the +existing pull mechanism for custom metrics, but instead to support additional +use cases not already supported. + +## API + +### Authentication, Segregation, and Flow Control + +Producers will be authenticated similarly to the way Heapster currently +authenticates clients: producers will present a certificate signed by a CA, and +Heapster will be configurable to only allow certain names to push metrics, or +to allow any certificate signed by the CA. + +Additionally, the name presented during authentication will be used as a prefix +for all metrics added. This will prevent two different metrics producers from +accidentally overwriting each other's custom metrics (otherwise, push metrics +will be stored and retrieved identically to pull-based custom metrics). + +In order to prevent push metrics from overwhelming the Heapster instance, it +will be possible to limit the total number of custom metrics each producer is +allowed to add, and the frequency at which producers are allowed to push new +sets of custom metrics. By default, no limits will be enforced unless +explicitly set via command line arguments. + +### Paths + +To add metrics, metrics producers will `POST` new metrics to +`/api/v1/push/{format}/{subpath}` in the format specified by `{format}`. The +metrics are pushed in bulk -- it is up to the format to support a way to name +specific metrics, namespaces, pods, and containers (for instance, the +Prometheus format uses metric names and labels for this purpose). The +`{subpath}` option enables different formats to have format specific sub-paths. + +### Metrics Format + +The underlying design can support multiple format "backends". The initial +backend, detailed here, will be based on the Prometheus format, and will be +available at `/api/v1/push/prometheus/`, +`/api/v1/push/prometheus/metrics/job/{producer_name}`, or +`/api/v1/push/promethus/metrics/jobs/{producer_name}`. If either of the latter +two paths are used, `{producer_name}` should match the name used when +authenticating with Heapster. + +Both the Prometheus text and protobuf formats will be supported. + +The metric name specified on each metric line will be used as the custom metric +name in Heapster (except prefixed as discussed in the "Authentication" +section). The following labels will be used to determine which object a metric +is associated with: + +- For kubernetes-related metrics, the `namespace` label will indicate + namespace, the `pod` label will indicate pod, and the `container` label will + indicate container. If only `namespace` is present, then the metric will be + considered namespace-level. If `namespace` and `pod` are present, then the + metric will be considered pod-level. If all three are present, the metric + will be considered container-level. + +- For kubernetes-related metrics, the `service` label can be used in + conjunction with the `namespace` label to indicate a service-level metric. + Currently, this will be stored in Heapster as a namespace-level metric + prefixed with the service name. + +- For non-kubernetes-related metrics, the `node` label will indicate node, and + the `container` label will indicate free container. If only `node` is + present, the metric will be considered node-level. Otherwise, the metric + will be considered free-container-level. + +If the `node`, `namespace`, `pod`, and `container`, and `service` labels are +not present in one of the configurations listed above, the metric line is +invalid and the batch should be rejected. + +Any additional labels will be treated the same as labels on existing custom +metrics (currently multiple custom metrics with the same name, but different +labels, are ignored, but this seems like an oversight and should probably be +fixed). + +Timestamps will be assigned by using the next Heapster metrics batch timestamp +after the time at which the metrics are received. If a timestamp is provided +as part of the metric line, this may be stored as a separate field for +posterity, but the "official" timestamp will be that of the assigned batch. + +As with normal Prometheus metrics, the `TYPE` line should be used to provide +the type of the metric. + +#### Example + +Suppose a producer with the name "http_gatherer" sent the following metrics to +`/api/v1/push/prometheus`: + +``` +# This is a pod-level metric (it might be used for autoscaling) +# TYPE http_requests_per_second guage +http_requests_per_minute{namespace="webapp",pod="frontend-server-a-1"} 20 +http_requests_per_minute{namespace="webapp",pod="frontend-server-a-2"} 5 +http_requests_per_minute{namespace="webapp",pod="frontend-server-b-1"} 25 + +# This is a service-level metric, which will be stored as frontend_hits_total +# and restapi_hits_total (these might be used for auto-idling) +# TYPE hits_total counter +hits_total{namespace="webapp",service="frontend"} 5000 +hits_total{namespace="webapp",service="restapi"} 6000 +``` + +This would result in the metrics being available at: + +``` +/api/v1/model/namespaces/webapp/pods/frontend-server-a-1/metrics/custom/http_gatherer/http_requests_per_minute +/api/v1/model/namespaces/webapp/pods/frontend-server-a-2/metrics/custom/http_gatherer/http_requests_per_minute +/api/v1/model/namespaces/webapp/pods/frontend-server-b-1/metrics/custom/http_gatherer/http_requests_per_minute +/api/v1/model/namespaces/webapp/metrics/custom/http_gatherer/frontend/hits_total +/api/v1/model/namespaces/webapp/metrics/custom/http_gatherer/restapi/hits_total +``` + +## Discussed Alternatives + +A number of alternatives came up during the discussion of this proposal. They +are discussed briefly below. Note that most of these alternatives do not deal +particularly well with a case where metrics need to come from a source that is +not running as a pod on the cluster. While it is expected that many of the +producers will be running as components on the cluster (e.g. as DaemonSets or +PetSets), it could still be adventageous to support metrics coming from +components that are not in the form of pods. + +### Writing directly into sinks + +This alternative would have producers write directly into sinks in the Heapster +storage schema, and then use a mechanism similar to the Oldtimer API to read +the metrics back. + +This would require every producer to know how to talk to every sink, would make +configuring the sinks more complicated, and would most likely lead to software +only being able to talk to one of the sinks supported by Heapster. +Additionally, you lose the benefits of the Heapster model, and either have to +adapt the existing Heapster model to fall back to an Oldtimer-like approach, or +teach all cluster components to be able to read from both the Heapster model +and Oldtimer simultaneously. + +### Reworking the existing cAdvisor-Kubelet-Heapster Pull Mechanism + +This alternative would involve reworking the existing pull mechanism to allow +certain pods to produce metrics that describe other resources besides the +themselves, as opposed to the current situation, where all custom metrics +collected via the current pull mechanism are marked as describing the producer +pod. + +This would require a mechanism for indicating to Heapster which pod names were +allowed to produce metrics that describe other resources, since admins would +generally want most pods producing metrics to continue to just have metrics +which describe only the producer pod. It would also conceptually blend +together pods producing metrics about themselves versus pods producing metrics +about others. Additionally, the current cAdvisor-based custom metrics +collection is not secured, so all metrics would be available to anyone with +knowlege of the appropriate port, but this may change in the future. + +### Using a new daemon per node to produce metrics + +This alternative would involve running a daemon on each node that aggregated +all the separate custom metrics producers' results together. + +It was suggested that an approach similar to the Prometheus Node Exporter +Textfile Collector could be used, in which sources would write their metrics to +files in a directly, which would later be read by the collector when polled for +metrics. When the producers are containerized, you'd need to use a hostPath +volume, have the daeamon look for specific emptyDir mounts in containers (and +use one director per container), or something similar. + +Alternatively, a new daemon could be run on each node that was responsible for +collecting metrics from producers who produce bulk metrics describing other +resources. + +This would still require some sort of auth to limit which pods where allowed to +do so (while the scoping above prevents collision, cluster admins would most +likely still want to limit which pods are allowed to post metrics which appear +in another pod's list of custom metrics). Unlike the proposal above, admins +could not simply rely on "whoever is allowed to authenticate" rule, since +cAdvisor does not check certificates like the normal Heapster auth mechanism. + +Additionally, this adds a bit of complexity on the producer's side, since it +requires continuously serving the metrics (this could be made easier by +providing a tool like the Prometheus Node Exporter Textfile Collector, which +just serves up metrics based on text files in a directory). + +### Adding an additional standard pull mechanism + +This alternative would involve writing a pull mechanim which, for instance, was +just able to read Prometheus metrics directly. This would either require +admins to configure the Heapster instance to know about every custom metrics +source (and restart Heapster when a new source needed to be added), or would +require teaching Heapster how to look for an annotation on certain pods to +determine which pods to query (Heapster would have a list-watch on the pods, +and look for pods added/removed/changed with the appropriate annotation). + +When used in the latter form, this mechanism would still likely require a +similar auth setup to the one proposed above, in order to allow the admin to +restrict which pods actually were allowed to produce the metrics. It also has +similar restrictions/disadvantages as the "Using a new daemon per node" method +discussed above. diff --git a/vendor/k8s.io/heapster/docs/proposals/vision.md b/vendor/k8s.io/heapster/docs/proposals/vision.md new file mode 100644 index 0000000000..88b9ab78f9 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/proposals/vision.md @@ -0,0 +1,130 @@ +# Heapster Long Term Vision + +## Current status + +Heapster is an important component of Kubernetes that is responsible for metrics and event +handling. It reads metrics from cluster nodes and writes them to external, permanent +storage. This is the main use case of Heapster. + +To support system components of Kubernetes Heapster calculates aggregated metrics (like +sum of containers' CPU usage in a pod) and long term statistics (average, 95percentile +with 1h resolution), keeps them in memory and exposes via Heapster API. This API is mainly +used by Horizontal Pod Autoscaler which asks for the most recent performance related +metrics to adjust the number of pods to the incoming traffic. The API is also used by KubeDash +and will be used by the new UI (which will replace KubeDash) as well. + +Additionally Heapster API allows to list all active nodes, namespaces, pods, containers +etc. present in the system. + +There is also a HeapsterGKE API dedicated for GKE through which it’s possible to get a full +dump of all metrics (spanning last minute or two). + +Metrics are gathered from cluster nodes, but Heapster developers wanted it to be useful also +in non-Kubernetes clusters. They wrote Heapster in a such a way that metrics can be read not +only from Kubernetes nodes (via Kubelet API) but also from custom deployments via cAdvisor +(with support for CoreOS Fleet and flat file node lists). + +Metrics collected by Heapster can be written into multiple kinds of storage - Influxdb, +OpenTSDB, Google Cloud Monitoring, Hawkular, Kafka, Riemann, ElasticSearch (some of them are +not yet submitted). + +In addition to gathering metrics Heapster is responsible for handling Kubernetes events - it +reads them from Kubernetes API server and writes them, without extra processing, to a selection +of persistent storages: Google Cloud Logging, Influxdb, Kafka, OpenTSDB, Hawkular, +ElasticSearch, etc. + +There is/was a plan to add resource prediction components (Initial Resources, Vertical +Pod Autoscaling) to Heapster binary. + +## Separation of Use Cases +From the current state description (see above) the following use cases can be extracted: + +* [UC1] Read metrics from nodes and write them to an external storage. +* [UC2] Expose metrics from the last 2-3 minutes (for HPA and GKE) +* [UC3] Read Events from the API server and write them to a permanent storage +* [UC4] Do some long-term (hours, days) metrics analysis to get stats (average, 95 percentile) +and expected resource usage. +* [UC5] Provide cpu and memory metrics for longer time window for the new Kubernetes +Dashboard UI (15 min for 1.2, up to 1h later for plots) + +UC1 and UC2 go together - to expose the most recent metrics the API should be connected +to the metrics stream. +UC3 can be completely separated from UC1, UC2 and UC4 - it reads different data from a +different place and writes it in a slightly different format to different sinks. +UC4 is connected to UC1 and UC2 but it is more based on data from the permanent storage +than on the super-fresh metrics stored in the memory. +UC5 can go either with UC1/UC2 or with UC4. As there is no immediate need for UC4 we will + provide basic UC5 together with UC1/UC2 but in the future it will join UC4. + +This separation leads to an idea of splitting Heapster into 3 binaries: + +* Core Heapster - covering UC1, UC2 and temporarily UC5 +* Eventer - covering UC3 +* Oldtimer - covering UC4 and UC5 + +## Reduction of Responsibility + +With 3 possible node sources (Kuberentes API Server, flat file, CoreOS Fleet), 2 metrics +sources (cAdvisor and Kubelet) and constantly growing number of sinks we have to separate +the stuff that the core Heapster/K8S team is responsible for and what is provided as a +plugin/addition and doesn’t come in the main release package. + +We decided to focus only on: + +Kubernetes API Server node source +Kubelet metrics source +Influxdb, GCM, GKE (there is special endpoint for GKE that exposes all available metrics), + Hawkular sinks for Heapster +Influxdb, GCL sinks for Eventer + +The rest of the sources/sinks will be available as plugins. The plugin will be used in 2 flavors: + +* Complied in - will require the user to rebuild the package and create his own image with +the desired set of plugins. +* Side-car - Heapster will talk to plugin’s HTTP server to get/pass metrics through a well +defined json interface. The plugin runs in a separate container. + +K8s team will explicitly say that it is NOT giving any warranty on the plugins. Plugins e2e +tests can be included in some CI suite but we will not block our development (too much) if +something breaks Kafka or Riemann. We will also not pay attention to whether a particular sink scales up. + +For now we will keep all of the currently available sinks compiled-in by default, to keep the +new Heapster more or less compatible with the old one, but eventually (if the number of sinks grows) + we will migrate some of them to plugins. + +## Custom Metrics Status + +Heapster is not a generic solution for gathering arbitrary number of arbitrary-formated custom +metrics. The support for custom metrics is focused on auto-scaling and critical functionality +monitoring (and potentially scheduling). And Heapster is oriented towards system metrics, not +application/business level metrics. + +Kubernetes users and application developers will be able to push any number of their custom +metrics through our pipeline to the storage but this should be considered as a bonus/best effort +functionality. Custom metrics will not influence our performance targets (no extra fine-tunning effort +to support >5 custom metrics per pod). There will be a flag in Kubelet that will limit the +number of custom metrics. + +## Performance Target + +Heapster product family (Core, Eventer and Oldtimer) should follow the same performance goals +as core Kubernetes. As Eventer is fairly simple and Oldtimer not yet fully defined this section +will focus only on Core Heapster (for metrics). + +For 1.2 we should scale to 1000 nodes each running at least 30 pods (100 for 1.3) each reporting +20 metrics every 1 min (30 sec preferably). That brings us to the number of 600k metrics +per minute and 10k metrics per second. + +Stretch goal (for 1.2/1.3) is 60k metrics per second (possibly with not everything being written to Influxdb). +On smaller deployments, like 500 nodes with 15-30 pods each it should be easy to have 30 sec +metrics resolution or smaller. + +Memory target - Fit into 2 gb with 1000 nodes x 30 pods and 6 gb with 1000 node x 100 pods (~60kb per pod). + +Latency, measured from the time when we initiate scraping metrics to the moment the metric +change is visible in the API, should be less than 1*metrics resolution, which mainly depends +on how fast it is possible to get all the metrics through the wire and parse them. + +The e2e latency from the moment the metric changes in the container to the moment the change is +visible in Heapster API is: metric_resolution + heapster_latency. + diff --git a/vendor/k8s.io/heapster/docs/riemann.md b/vendor/k8s.io/heapster/docs/riemann.md new file mode 100644 index 0000000000..1d652812b9 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/riemann.md @@ -0,0 +1,20 @@ +# Run Heapster with a Riemann back end + +[Riemann](https://riemann.io) is an event processing engine which allows +sophisticated transformation, analysis, and routing of events. Configuration is +provided using Clojure. When Heapster is configured with a Riemann sink, it +will stream Riemann-format events to a separate Riemann instance. + +The following sink options are supported as URL parameters: + host: default "riemann-heapster:5555" + The host-port connection string for the Riemann instance. + ttl: default 60.0 + The default TTL (in seconds) assigned to Riemann events + state: default nil + The default state (string) assigned to Riemann events + tags: default [] + An array of strings that should be associated with each Riemann event + storeEvents: default true + Whether Kubernetes events should be forwarded to Riemann + +See the [sample config](../riemann/riemann-pagerduty-sample.config) for configuration pointers. diff --git a/vendor/k8s.io/heapster/docs/sink-configuration.md b/vendor/k8s.io/heapster/docs/sink-configuration.md new file mode 100644 index 0000000000..0d6152ef98 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/sink-configuration.md @@ -0,0 +1,200 @@ +Configuring sinks +================= + +Heapster can store data into different backends (sinks). These are specified on the command line +via the `--sink` flag. The flag takes an argument of the form `PREFIX:CONFIG[?OPTIONS]`. +Options (optional!) are specified as URL query parameters, separated by `&` as normal. +This allows each source to have custom configuration passed to it without needing to +continually add new flags to Heapster as new sinks are added. This also means +heapster can store data into multiple sinks at once. + +## Current sinks + +### Log + +This sinks writes all data to the standard output which is particularly useful for debugging. + + --sink=log + +### InfluxDB +This sink supports both monitoring metrics and events. +*Supports InfluxDB versions v0.9 and above* +To use the InfluxDB sink add the following flag: + + --sink=influxdb:[?] + +If you're running Heapster in a Kubernetes cluster with the default InfluxDB + Grafana setup you can use the following flag: + + --sink=influxdb:http://monitoring-influxdb:80/ + +The following options are available: +* `user` - InfluxDB username (default: `root`) +* `pw` - InfluxDB password (default: `root`) +* `db` - InfluxDB Database name (default: `k8s`) +* `secure` - Connect securely to InfluxDB (default: `false`) +* `withfields` - Use [InfluxDB fields](storage-schema.md#using-fields) (default: `false`) + +### Google Cloud Monitoring +This sink supports monitoring metrics only. +To use the GCM sink add the following flag: + + --sink=gcm + +*Note: This sink works only on a Google Compute Enginer VM as of now* + +GCM has one option - `metrics` that can be set to: +* all - the sink exports all metrics +* autoscaling - the sink exports only autoscaling-related metrics + +### Google Cloud Logging +This sink supports events only. +To use the InfluxDB sink add the following flag: + + --sink=gcl + +*Notes:* + * This sink works only on a Google Compute Enginer VM as of now + * GCE instance must have “https://www.googleapis.com/auth/logging.write” auth scope + * GCE instance must have Logging API enabled for the project in Google Developer Console + * GCL Logs are accessible via: + * `https://console.developers.google.com/project//logs?service=custom.googleapis.com` + * Where `project_ID` is the project ID of the Google Cloud Platform project ID. + * Select `kubernetes.io/events` from the `All logs` drop down menu. + +### Hawkular-Metrics +This sink supports monitoring metrics only. +To use the Hawkular-Metrics sink add the following flag: + + --sink=hawkular:[?] + +If `HAWKULAR_SERVER_URL` includes any path, the default `hawkular/metrics` is overridden. To use SSL, the `HAWKULAR_SERVER_URL` has to start with `https` + +The following options are available: + +* `tenant` - Hawkular-Metrics tenantId (default: `heapster`) +* `labelToTenant` - Hawkular-Metrics uses given label's value as tenant value when storing data +* `useServiceAccount` - Sink will use the service account token to authorize to Hawkular-Metrics (requires OpenShift) +* `insecure` - SSL connection will not verify the certificates +* `caCert` - A path to the CA Certificate file that will be used in the connection +* `auth` - Kubernetes authentication file that will be used for constructing the TLSConfig +* `user` - Username to connect to the Hawkular-Metrics server +* `pass` - Password to connect to the Hawkular-Metrics server +* `filter` - Allows bypassing the store of matching metrics, any number of `filter` parameters can be given with a syntax of `filter=operation(param)`. Supported operations and their params: + * `label` - The syntax is `label(labelName:regexp)` where `labelName` is 1:1 match and `regexp` to use for matching is given after `:` delimiter + * `name` - The syntax is `name(regexp)` where MetricName is matched (such as `cpu/usage`) with a `regexp` filter +* `batchSize`- How many metrics are sent in each request to Hawkular-Metrics (default is 1000) +* `concurrencyLimit`- How many concurrent requests are used to send data to the Hawkular-Metrics (default is 5) + +A combination of `insecure` / `caCert` / `auth` is not supported, only a single of these parameters is allowed at once. Also, combination of `useServiceAccount` and `user` + `pass` is not supported. To increase the performance of Hawkular sink in case of multiple instances of Hawkular-Metrics (such as scaled scenario in OpenShift) modify the parameters of batchSize and concurrencyLimit to balance the load on Hawkular-Metrics instances. + +### OpenTSDB +This sink supports monitoring metrics and events. +To use the opentsdb sink add the following flag: + + --sink=opentsdb: + +Currently, accessing opentsdb via its rest apis doesn't need any authentication, so you +can enable opentsdb sink like this: + + --sink=opentsdb:http://192.168.1.8:4242 + +### Monasca +This sink supports monitoring metrics only. +To use the Monasca sink add the following flag: + + --sink=monasca:[?] + +The available options are listed below, and some of them are mandatory. You need to provide access to the Identity service of OpenStack (keystone). +Currently, only authorization through `username` / `userID` + `password` / `APIKey` is supported. + +The Monasca sink is then created with either the provided Monasca API Server URL, or the URL is discovered automatically if none is provided by the user. + +The following options are available: + +* `user-id` - ID of the OpenStack user +* `username` - Name of the OpenStack user +* `tenant-id` - ID of the OpenStack tenant (project) +* `keystone-url` - URL to the Keystone identity service (*mandatory*). Must be a v3 server (required by Monasca) +* `password` - Password of the OpenStack user +* `api-key` - API-Key for the OpenStack user +* `domain-id` - ID of the OpenStack user's domain +* `domain-name` - Name of the OpenStack user's domain +* `monasca-url` - URL of the Monasca API server (*optional*: the sink will attempt to discover the service if not provided) + +### Kafka +This sink supports monitoring metrics only. +To use the kafka sink add the following flag: + + --sink="kafka:>" + +Normally, kafka server has multi brokers, so brokers' list need be configured for producer. +So, we provide kafka brokers' list and topics about timeseries & topic in url's query string. +Options can be set in query string, like this: + +* `brokers` - Kafka's brokers' list. +* `timeseriestopic` - Kafka's topic for timeseries. Default value : `heapster-metrics` +* `eventstopic` - Kafka's topic for events.Default value : `heapster-events` + +For example, + + --sink="kafka:?brokers=localhost:9092&brokers=localhost:9093×eriestopic=testseries&eventstopic=testtopic" + +### Riemann +This sink supports metrics only. +To use the reimann sink add the following flag: + + --sink="riemann:[?]" + +The following options are available: + +* `ttl` - TTL for writes to Riemann. Default: `60 seconds` +* `state` - FIXME. Default: `""` +* `tags` - FIXME. Default. `none` +* `storeEvents` - Control storage of events. Default: `true` + +### Elasticsearch +This sink supports monitoring metrics and events. To use the ElasticSearch +sink add the following flag: + + --sink=elasticsearch:[?] + +Normally an ElasticSearch cluster has multiple nodes or a proxy, so these need +to be configured for the ElasticSearch sink. To do this, you can set +`ES_SERVER_URL` to a dummy value, and use the `?nodes=` query value for each +additional node in the cluster. For example: + + --sink=elasticsearch:?nodes=foo.com:9200&nodes=bar.com:9200 + +Besides this, the following options can be set in query string: + +* `Index` - the index for metrics and events. The default is `heapster` +* `esUserName` - the username if authentication is enabled +* `esUserSecret` - the password if authentication is enabled +* `maxRetries` - the number of retries that the Elastic client will perform + for a single request after before giving up and return an error. It is `0` + by default, so retry is disabled by default. +* `healthCheck` - specifies if healthchecks are enabled by default. It is enabled + by default. To disable, provide a negative boolean value like `0` or `false`. +* `sniff` - specifies if the sniffer is enabled by default. It is enabled + by default. To disable, provide a negative boolean value like `0` or `false`. +* `startupHealthcheckTimeout` - the time in seconds the healthcheck waits for + a response from Elasticsearch on startup, i.e. when creating a client. The + default value is `1`. + +Like this: + + --sink="elasticsearch:?nodes=0.0.0.0:9200&Index=testMetric" + + or + + --sink="elasticsearch:?nodes=0.0.0.0:9200&Index=testEvent" + +## Using multiple sinks + +Heapster can be configured to send k8s metrics and events to multiple sinks by specifying the`--sink=...` flag multiple times. + +For example, to send data to both gcm and influxdb at the same time, you can use the following: + +```shell + --sink=gcm --sink=influxdb:http://monitoring-influxdb:80/ +``` diff --git a/vendor/k8s.io/heapster/docs/source-configuration.md b/vendor/k8s.io/heapster/docs/source-configuration.md new file mode 100644 index 0000000000..3e8e0f0411 --- /dev/null +++ b/vendor/k8s.io/heapster/docs/source-configuration.md @@ -0,0 +1,87 @@ +Configuring sources +=================== + +Heapster can get data from multiple sources (although at this momeent we support only one kind - Kubernetes). +They are specified in the command line via the `--source` flag. The flag takes an argument of the form `PREFIX:CONFIG[?OPTIONS]`. +Options (optional!) are specified as URL query parameters, separated by `&` as normal. +This allows each source to have custom configuration passed to it without needing to +continually add new flags to Heapster as new sources are added. This also means +Heapster can capture metrics from multiple sources at once, potentially even multiple +Kubernetes clusters. + +## Current sources +### Kubernetes +To use the kubernetes source add the following flag: + + --source=kubernetes:[?] + +If you're running Heapster in a Kubernetes pod you can use the following flag: + + --source=kubernetes + +Heapster requires an authentication token to connect with the apiserver securely. By default, Heapster will use the inClusterConfig system to configure the secure connection. This requires kubernetes version `v1.0.3` or higher and a couple extra kubernetes configuration steps. Firstly, for your apiserver you must create a SSL certificate pair with a SAN that includes the ClusterIP of the kubernetes service. Look [here](https://github.com/kubernetes/kubernetes/blob/e4fde6d2cae2d924a4eb72d1e3b2639f057bb8c1/cluster/gce/util.sh#L497-L559) for an example of how to properly generate certs. Secondly, you need to pass the `ca.crt` that you generated to the `--root-ca-file` option of the controller-manager. This will distribute the root CA to `/var/run/secrets/kubernetes.io/serviceaccount/` of all pods. If you are using `ABAC` authorization (as opposed to `AllowAll` which is the default), you will also need to give the `system:serviceaccount::default` readonly access to the cluster (look [here](https://github.com/kubernetes/kubernetes/blob/master/docs/admin/authorization.md#a-quick-note-on-service-accounts) for more info). + +If you don't want to setup inClusterConfig, you can still use Heapster! To run without auth, use the following config: + + --source=kubernetes:http://:?inClusterConfig=false + +This requires the apiserver to be setup completely without auth, which can be done by binding the insecure port to all interfaces (see the apiserver `--insecure-bind-address` option) but *WARNING* be aware of the security repercussions. Only do this if you trust *EVERYONE* on your network. + +*Note: Remove "monitoring-token" volume from heaspter controller config if you are running without auth.* + +Alternatively, you can use a heapster-only serviceaccount like this: + +```shell +cat 5 { + t.Fatalf("Wrong number of exports executed: %d", sink.GetExportCount()) + } +} diff --git a/vendor/k8s.io/heapster/events/sinks/elasticsearch/driver.go b/vendor/k8s.io/heapster/events/sinks/elasticsearch/driver.go new file mode 100644 index 0000000000..be2204df72 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/elasticsearch/driver.go @@ -0,0 +1,113 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package elasticsearch + +import ( + "net/url" + "sync" + "time" + + "encoding/json" + + "github.com/golang/glog" + "github.com/olivere/elastic" + esCommon "k8s.io/heapster/common/elasticsearch" + event_core "k8s.io/heapster/events/core" + "k8s.io/heapster/metrics/core" + kube_api "k8s.io/kubernetes/pkg/api" +) + +const ( + typeName = "events" +) + +// LimitFunc is a pluggable function to enforce limits on the object +type SaveDataFunc func(esClient *elastic.Client, indexName string, typeName string, sinkData interface{}) error + +type elasticSearchSink struct { + saveDataFunc SaveDataFunc + esConfig esCommon.ElasticSearchConfig + sync.RWMutex +} + +type EsSinkPoint struct { + EventValue interface{} + EventTimestamp time.Time + EventTags map[string]string +} + +// Generate point value for event +func getEventValue(event *kube_api.Event) (string, error) { + // TODO: check whether indenting is required. + bytes, err := json.MarshalIndent(event, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} + +func eventToPoint(event *kube_api.Event) (*EsSinkPoint, error) { + value, err := getEventValue(event) + if err != nil { + return nil, err + } + point := EsSinkPoint{ + EventTimestamp: event.LastTimestamp.Time.UTC(), + EventValue: value, + EventTags: map[string]string{ + "eventID": string(event.UID), + }, + } + if event.InvolvedObject.Kind == "Pod" { + point.EventTags[core.LabelPodId.Key] = string(event.InvolvedObject.UID) + point.EventTags[core.LabelPodName.Key] = event.InvolvedObject.Name + } + point.EventTags[core.LabelHostname.Key] = event.Source.Host + return &point, nil +} + +func (sink *elasticSearchSink) ExportEvents(eventBatch *event_core.EventBatch) { + sink.Lock() + defer sink.Unlock() + for _, event := range eventBatch.Events { + point, err := eventToPoint(event) + if err != nil { + glog.Warningf("Failed to convert event to point: %v", err) + } + sink.saveDataFunc(sink.esConfig.EsClient, sink.esConfig.Index, typeName, point) + } +} + +func (sink *elasticSearchSink) Name() string { + return "ElasticSearch Sink" +} + +func (sink *elasticSearchSink) Stop() { + // nothing needs to be done. +} + +func NewElasticSearchSink(uri *url.URL) (event_core.EventSink, error) { + var esSink elasticSearchSink + elasticsearchConfig, err := esCommon.CreateElasticSearchConfig(uri) + if err != nil { + glog.V(2).Infof("failed to config elasticsearch") + return nil, err + } + + esSink.esConfig = *elasticsearchConfig + esSink.saveDataFunc = esCommon.SaveDataIntoES + glog.V(2).Infof("elasticsearch sink setup successfully") + return &esSink, nil +} diff --git a/vendor/k8s.io/heapster/events/sinks/elasticsearch/driver_test.go b/vendor/k8s.io/heapster/events/sinks/elasticsearch/driver_test.go new file mode 100644 index 0000000000..cdd7ddbc50 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/elasticsearch/driver_test.go @@ -0,0 +1,100 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package elasticsearch + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/olivere/elastic" + "github.com/stretchr/testify/assert" + esCommon "k8s.io/heapster/common/elasticsearch" + "k8s.io/heapster/events/core" + kube_api "k8s.io/kubernetes/pkg/api" + kube_api_unversioned "k8s.io/kubernetes/pkg/api/unversioned" +) + +type dataSavedToES struct { + data string +} + +type fakeESSink struct { + core.EventSink + savedData []dataSavedToES +} + +var FakeESSink fakeESSink + +func SaveDataIntoESStub(esClient *elastic.Client, indexName string, typeName string, sinkData interface{}) error { + jsonItems, err := json.Marshal(sinkData) + if err != nil { + return fmt.Errorf("failed to transform the items to json : %s", err) + } + FakeESSink.savedData = append(FakeESSink.savedData, dataSavedToES{string(jsonItems)}) + return nil +} + +// Returns a fake ES sink. +func NewFakeSink() fakeESSink { + savedData := make([]dataSavedToES, 0) + return fakeESSink{ + &elasticSearchSink{ + saveDataFunc: SaveDataIntoESStub, + esConfig: esCommon.ElasticSearchConfig{ + Index: "heapster-metric-index", + EsClient: &elastic.Client{}, + }, + }, + savedData, + } +} + +func TestStoreDataEmptyInput(t *testing.T) { + fakeSink := NewFakeSink() + dataBatch := core.EventBatch{} + fakeSink.ExportEvents(&dataBatch) + assert.Equal(t, 0, len(fakeSink.savedData)) +} + +func TestStoreMultipleDataInput(t *testing.T) { + fakeSink := NewFakeSink() + timestamp := time.Now() + now := time.Now() + event1 := kube_api.Event{ + Message: "event1", + Count: 100, + LastTimestamp: kube_api_unversioned.NewTime(now), + FirstTimestamp: kube_api_unversioned.NewTime(now), + } + event2 := kube_api.Event{ + Message: "event2", + Count: 101, + LastTimestamp: kube_api_unversioned.NewTime(now), + FirstTimestamp: kube_api_unversioned.NewTime(now), + } + data := core.EventBatch{ + Timestamp: timestamp, + Events: []*kube_api.Event{ + &event1, + &event2, + }, + } + fakeSink.ExportEvents(&data) + // expect msg string + assert.Equal(t, 2, len(FakeESSink.savedData)) + FakeESSink = fakeESSink{} +} diff --git a/vendor/k8s.io/heapster/events/sinks/factory.go b/vendor/k8s.io/heapster/events/sinks/factory.go new file mode 100644 index 0000000000..dc71a0017c --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/factory.go @@ -0,0 +1,63 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sinks + +import ( + "fmt" + + "k8s.io/heapster/common/flags" + "k8s.io/heapster/events/core" + "k8s.io/heapster/events/sinks/elasticsearch" + "k8s.io/heapster/events/sinks/gcl" + "k8s.io/heapster/events/sinks/influxdb" + "k8s.io/heapster/events/sinks/log" + + "github.com/golang/glog" +) + +type SinkFactory struct { +} + +func (this *SinkFactory) Build(uri flags.Uri) (core.EventSink, error) { + switch uri.Key { + case "gcl": + return gcl.CreateGCLSink(&uri.Val) + case "log": + return logsink.CreateLogSink() + case "influxdb": + return influxdb.CreateInfluxdbSink(&uri.Val) + case "elasticsearch": + return elasticsearch.NewElasticSearchSink(&uri.Val) + default: + return nil, fmt.Errorf("Sink not recognized: %s", uri.Key) + } +} + +func (this *SinkFactory) BuildAll(uris flags.Uris) []core.EventSink { + result := make([]core.EventSink, 0, len(uris)) + for _, uri := range uris { + sink, err := this.Build(uri) + if err != nil { + glog.Errorf("Failed to create sink: %v", err) + continue + } + result = append(result, sink) + } + return result +} + +func NewSinkFactory() *SinkFactory { + return &SinkFactory{} +} diff --git a/vendor/k8s.io/heapster/events/sinks/gcl/gcl.go b/vendor/k8s.io/heapster/events/sinks/gcl/gcl.go new file mode 100644 index 0000000000..2a4981c316 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/gcl/gcl.go @@ -0,0 +1,97 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gcl + +import ( + "fmt" + "net/url" + "time" + + gce_util "k8s.io/heapster/common/gce" + "k8s.io/heapster/events/core" + + "github.com/golang/glog" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + gcl "google.golang.org/api/logging/v2beta1" + gce "google.golang.org/cloud/compute/metadata" +) + +const ( + monitoredResourceType = "global" + logName = "kubernetes.io/events" + loggingSeverity = "NOTICE" +) + +type gclSink struct { + project string + gclService *gcl.Service +} + +func (sink *gclSink) ExportEvents(eventBatch *core.EventBatch) { + if len(eventBatch.Events) == 0 { + glog.V(4).Info("Not events to export") + return + } + glog.V(4).Info("Exporting events") + entries := make([]*gcl.LogEntry, len(eventBatch.Events)) + for i, event := range eventBatch.Events { + entries[i] = &gcl.LogEntry{ + LogName: fmt.Sprintf("projects/%s/logs/%s", sink.project, url.QueryEscape(logName)), + Timestamp: event.LastTimestamp.Time.UTC().Format(time.RFC3339), + Severity: loggingSeverity, + Resource: &gcl.MonitoredResource{Type: monitoredResourceType}, + InsertId: string(event.UID), + JsonPayload: *event, + } + } + req := &gcl.WriteLogEntriesRequest{Entries: entries} + if _, err := sink.gclService.Entries.Write(req).Do(); err != nil { + glog.Errorf("Error while exporting events to GCL: %v", err) + } else { + glog.V(4).Infof("Sucessfully exported %d events", len(entries)) + } +} + +func (sink *gclSink) Name() string { + return "GCL Sink" +} + +func (sink *gclSink) Stop() { + // nothing needs to be done. +} + +func CreateGCLSink(uri *url.URL) (core.EventSink, error) { + if err := gce_util.EnsureOnGCE(); err != nil { + return nil, err + } + + // Detect project ID + projectId, err := gce.ProjectID() + if err != nil { + return nil, err + } + + // Create Google Cloud Logging service. + client := oauth2.NewClient(oauth2.NoContext, google.ComputeTokenSource("")) + gclService, err := gcl.New(client) + if err != nil { + return nil, err + } + + sink := &gclSink{project: projectId, gclService: gclService} + glog.Info("created GCL sink") + return sink, nil +} diff --git a/vendor/k8s.io/heapster/events/sinks/influxdb/influxdb.go b/vendor/k8s.io/heapster/events/sinks/influxdb/influxdb.go new file mode 100644 index 0000000000..0b78ec3a49 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/influxdb/influxdb.go @@ -0,0 +1,223 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + "sync" + "time" + + influxdb_common "k8s.io/heapster/common/influxdb" + "k8s.io/heapster/events/core" + metrics_core "k8s.io/heapster/metrics/core" + kube_api "k8s.io/kubernetes/pkg/api" + + "github.com/golang/glog" + influxdb "github.com/influxdata/influxdb/client" +) + +type influxdbSink struct { + client influxdb_common.InfluxdbClient + sync.RWMutex + c influxdb_common.InfluxdbConfig + dbExists bool +} + +const ( + eventMeasurementName = "log/events" + // Event special tags + eventUID = "uid" + // Value Field name + valueField = "value" + // Event special tags + dbNotFoundError = "database not found" + + // Maximum number of influxdb Points to be sent in one batch. + maxSendBatchSize = 1000 +) + +func (sink *influxdbSink) resetConnection() { + glog.Infof("Influxdb connection reset") + sink.dbExists = false + sink.client = nil +} + +// Generate point value for event +func getEventValue(event *kube_api.Event) (string, error) { + // TODO: check whether indenting is required. + bytes, err := json.MarshalIndent(event, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} + +func eventToPointWithFields(event *kube_api.Event) (*influxdb.Point, error) { + point := influxdb.Point{ + Measurement: "events", + Time: event.LastTimestamp.Time.UTC(), + Fields: map[string]interface{}{ + "message": event.Message, + }, + Tags: map[string]string{ + eventUID: string(event.UID), + }, + } + if event.InvolvedObject.Kind == "Pod" { + point.Tags[metrics_core.LabelPodId.Key] = string(event.InvolvedObject.UID) + } + point.Tags["object_name"] = event.InvolvedObject.Name + point.Tags["type"] = event.Type + point.Tags["kind"] = event.InvolvedObject.Kind + point.Tags["component"] = event.Source.Component + point.Tags["reason"] = event.Reason + point.Tags[metrics_core.LabelNamespaceName.Key] = event.Namespace + point.Tags[metrics_core.LabelHostname.Key] = event.Source.Host + return &point, nil +} + +func eventToPoint(event *kube_api.Event) (*influxdb.Point, error) { + value, err := getEventValue(event) + if err != nil { + return nil, err + } + + point := influxdb.Point{ + Measurement: eventMeasurementName, + Time: event.LastTimestamp.Time.UTC(), + Fields: map[string]interface{}{ + valueField: value, + }, + Tags: map[string]string{ + eventUID: string(event.UID), + }, + } + if event.InvolvedObject.Kind == "Pod" { + point.Tags[metrics_core.LabelPodId.Key] = string(event.InvolvedObject.UID) + point.Tags[metrics_core.LabelPodName.Key] = event.InvolvedObject.Name + } + point.Tags[metrics_core.LabelHostname.Key] = event.Source.Host + return &point, nil +} + +func (sink *influxdbSink) ExportEvents(eventBatch *core.EventBatch) { + sink.Lock() + defer sink.Unlock() + + dataPoints := make([]influxdb.Point, 0, 10) + for _, event := range eventBatch.Events { + var point *influxdb.Point + var err error + if sink.c.WithFields { + point, err = eventToPointWithFields(event) + } else { + point, err = eventToPoint(event) + } + if err != nil { + glog.Warningf("Failed to convert event to point: %v", err) + } + dataPoints = append(dataPoints, *point) + if len(dataPoints) >= maxSendBatchSize { + sink.sendData(dataPoints) + dataPoints = make([]influxdb.Point, 0, 1) + } + } + if len(dataPoints) >= 0 { + sink.sendData(dataPoints) + } +} + +func (sink *influxdbSink) sendData(dataPoints []influxdb.Point) { + if err := sink.createDatabase(); err != nil { + glog.Errorf("Failed to create infuxdb: %v", err) + return + } + bp := influxdb.BatchPoints{ + Points: dataPoints, + Database: sink.c.DbName, + RetentionPolicy: "default", + } + + start := time.Now() + if _, err := sink.client.Write(bp); err != nil { + if strings.Contains(err.Error(), dbNotFoundError) { + sink.resetConnection() + } else if _, _, err := sink.client.Ping(); err != nil { + glog.Errorf("InfluxDB ping failed: %v", err) + sink.resetConnection() + } + } + end := time.Now() + glog.V(4).Infof("Exported %d data to influxDB in %s", len(dataPoints), end.Sub(start)) +} + +func (sink *influxdbSink) Name() string { + return "InfluxDB Sink" +} + +func (sink *influxdbSink) Stop() { + // nothing needs to be done. +} + +func (sink *influxdbSink) createDatabase() error { + if sink.client == nil { + client, err := influxdb_common.NewClient(sink.c) + if err != nil { + return err + } + sink.client = client + } + + if sink.dbExists { + return nil + } + q := influxdb.Query{ + Command: fmt.Sprintf("CREATE DATABASE %s", sink.c.DbName), + } + if resp, err := sink.client.Query(q); err != nil { + // We want to return error only if it is not "already exists" error. + if !(resp != nil && resp.Err != nil && strings.Contains(resp.Err.Error(), "already exists")) { + return fmt.Errorf("Database creation failed: %v", err) + } + } + sink.dbExists = true + glog.Infof("Created database %q on influxDB server at %q", sink.c.DbName, sink.c.Host) + return nil +} + +// Returns a thread-safe implementation of core.EventSink for InfluxDB. +func new(c influxdb_common.InfluxdbConfig) core.EventSink { + client, err := influxdb_common.NewClient(c) + if err != nil { + glog.Errorf("issues while creating an InfluxDB sink: %v, will retry on use", err) + } + return &influxdbSink{ + client: client, // can be nil + c: c, + } +} + +func CreateInfluxdbSink(uri *url.URL) (core.EventSink, error) { + config, err := influxdb_common.BuildConfig(uri) + if err != nil { + return nil, err + } + sink := new(*config) + glog.Infof("created influxdb sink with options: host:%s user:%s db:%s", config.Host, config.User, config.DbName) + return sink, nil +} diff --git a/vendor/k8s.io/heapster/events/sinks/influxdb/influxdb_test.go b/vendor/k8s.io/heapster/events/sinks/influxdb/influxdb_test.go new file mode 100644 index 0000000000..ee8e653f1a --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/influxdb/influxdb_test.go @@ -0,0 +1,104 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "testing" + "time" + + "net/http/httptest" + "net/url" + + "github.com/stretchr/testify/assert" + influxdb_common "k8s.io/heapster/common/influxdb" + "k8s.io/heapster/events/core" + kube_api "k8s.io/kubernetes/pkg/api" + kube_api_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + util "k8s.io/kubernetes/pkg/util/testing" +) + +type fakeInfluxDBEventSink struct { + core.EventSink + fakeDbClient *influxdb_common.FakeInfluxDBClient +} + +func NewFakeSink() fakeInfluxDBEventSink { + return fakeInfluxDBEventSink{ + &influxdbSink{ + client: influxdb_common.Client, + c: influxdb_common.Config, + }, + influxdb_common.Client, + } +} + +func TestStoreDataEmptyInput(t *testing.T) { + fakeSink := NewFakeSink() + eventBatch := core.EventBatch{} + fakeSink.ExportEvents(&eventBatch) + assert.Equal(t, 0, len(fakeSink.fakeDbClient.Pnts)) +} + +func TestStoreMultipleDataInput(t *testing.T) { + fakeSink := NewFakeSink() + timestamp := time.Now() + + now := time.Now() + event1 := kube_api.Event{ + Message: "event1", + Count: 100, + LastTimestamp: kube_api_unversioned.NewTime(now), + FirstTimestamp: kube_api_unversioned.NewTime(now), + } + + event2 := kube_api.Event{ + Message: "event2", + Count: 101, + LastTimestamp: kube_api_unversioned.NewTime(now), + FirstTimestamp: kube_api_unversioned.NewTime(now), + } + + data := core.EventBatch{ + Timestamp: timestamp, + Events: []*kube_api.Event{ + &event1, + &event2, + }, + } + + fakeSink.ExportEvents(&data) + assert.Equal(t, 2, len(fakeSink.fakeDbClient.Pnts)) +} + +func TestCreateInfluxdbSink(t *testing.T) { + handler := util.FakeHandler{ + StatusCode: 200, + RequestBody: "", + ResponseBody: "", + T: t, + } + server := httptest.NewServer(&handler) + defer server.Close() + + stubInfluxDBUrl, err := url.Parse(server.URL) + assert.NoError(t, err) + + //create influxdb sink + sink, err := CreateInfluxdbSink(stubInfluxDBUrl) + assert.NoError(t, err) + + //check sink name + assert.Equal(t, sink.Name(), "InfluxDB Sink") +} diff --git a/vendor/k8s.io/heapster/events/sinks/log/log_sink.go b/vendor/k8s.io/heapster/events/sinks/log/log_sink.go new file mode 100644 index 0000000000..41865d2664 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/log/log_sink.go @@ -0,0 +1,51 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logsink + +import ( + "bytes" + "fmt" + + "github.com/golang/glog" + "k8s.io/heapster/events/core" +) + +type LogSink struct { +} + +func (this *LogSink) Name() string { + return "LogSink" +} + +func (this *LogSink) Stop() { + // Do nothing. +} + +func batchToString(batch *core.EventBatch) string { + var buffer bytes.Buffer + buffer.WriteString(fmt.Sprintf("EventBatch Timestamp: %s\n", batch.Timestamp)) + for _, event := range batch.Events { + buffer.WriteString(fmt.Sprintf(" %s (cnt:%d): %s\n", event.LastTimestamp, event.Count, event.Message)) + } + return buffer.String() +} + +func (this *LogSink) ExportEvents(batch *core.EventBatch) { + glog.Info(batchToString(batch)) +} + +func CreateLogSink() (*LogSink, error) { + return &LogSink{}, nil +} diff --git a/vendor/k8s.io/heapster/events/sinks/log/log_sink_test.go b/vendor/k8s.io/heapster/events/sinks/log/log_sink_test.go new file mode 100644 index 0000000000..68002cde61 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/log/log_sink_test.go @@ -0,0 +1,49 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logsink + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + kube_api "k8s.io/kubernetes/pkg/api" + kube_api_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + + "k8s.io/heapster/events/core" +) + +func TestSimpleWrite(t *testing.T) { + now := time.Now() + event := kube_api.Event{ + Message: "bzium", + Count: 251, + LastTimestamp: kube_api_unversioned.NewTime(now), + FirstTimestamp: kube_api_unversioned.NewTime(now), + } + batch := core.EventBatch{ + Timestamp: now, + Events: []*kube_api.Event{&event}, + } + + log := batchToString(&batch) + fmt.Printf(log) + + assert.True(t, strings.Contains(log, "bzium")) + assert.True(t, strings.Contains(log, "251")) + assert.True(t, strings.Contains(log, fmt.Sprintf("%s", now))) +} diff --git a/vendor/k8s.io/heapster/events/sinks/manager.go b/vendor/k8s.io/heapster/events/sinks/manager.go new file mode 100644 index 0000000000..0a22a2cd79 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/manager.go @@ -0,0 +1,144 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sinks + +import ( + "sync" + "time" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/heapster/events/core" +) + +const ( + DefaultSinkExportEventsTimeout = 20 * time.Second + DefaultSinkStopTimeout = 60 * time.Second +) + +var ( + // Time spent exporting events to sink in microseconds. + exporterDuration = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "eventer", + Subsystem: "exporter", + Name: "duration_microseconds", + Help: "Time spent exporting events to sink in microseconds.", + }, + []string{"exporter"}, + ) +) + +func init() { + prometheus.MustRegister(exporterDuration) +} + +type sinkHolder struct { + sink core.EventSink + eventBatchChannel chan *core.EventBatch + stopChannel chan bool +} + +// Sink Manager - a special sink that distributes data to other sinks. It pushes data +// only to these sinks that completed their previous exports. Data that could not be +// pushed in the defined time is dropped and not retried. +type sinkManager struct { + sinkHolders []sinkHolder + exportEventsTimeout time.Duration + // Should be larger than exportEventsTimeout, although it is not a hard requirement. + stopTimeout time.Duration +} + +func NewEventSinkManager(sinks []core.EventSink, exportEventsTimeout, stopTimeout time.Duration) (core.EventSink, error) { + sinkHolders := []sinkHolder{} + for _, sink := range sinks { + sh := sinkHolder{ + sink: sink, + eventBatchChannel: make(chan *core.EventBatch), + stopChannel: make(chan bool), + } + sinkHolders = append(sinkHolders, sh) + go func(sh sinkHolder) { + for { + select { + case data := <-sh.eventBatchChannel: + export(sh.sink, data) + case isStop := <-sh.stopChannel: + glog.V(2).Infof("Stop received: %s", sh.sink.Name()) + if isStop { + sh.sink.Stop() + return + } + } + } + }(sh) + } + return &sinkManager{ + sinkHolders: sinkHolders, + exportEventsTimeout: exportEventsTimeout, + stopTimeout: stopTimeout, + }, nil +} + +// Guarantees that the export will complete in exportEventsTimeout. +func (this *sinkManager) ExportEvents(data *core.EventBatch) { + var wg sync.WaitGroup + for _, sh := range this.sinkHolders { + wg.Add(1) + go func(sh sinkHolder, wg *sync.WaitGroup) { + defer wg.Done() + glog.V(2).Infof("Pushing events to: %s", sh.sink.Name()) + select { + case sh.eventBatchChannel <- data: + glog.V(2).Infof("Data events completed: %s", sh.sink.Name()) + // everything ok + case <-time.After(this.exportEventsTimeout): + glog.Warningf("Failed to events data to sink: %s", sh.sink.Name()) + } + }(sh, &wg) + } + // Wait for all pushes to complete or timeout. + wg.Wait() +} + +func (this *sinkManager) Name() string { + return "Manager" +} + +func (this *sinkManager) Stop() { + for _, sh := range this.sinkHolders { + glog.V(2).Infof("Running stop for: %s", sh.sink.Name()) + + go func(sh sinkHolder) { + select { + case sh.stopChannel <- true: + // everything ok + glog.V(2).Infof("Stop sent to sink: %s", sh.sink.Name()) + + case <-time.After(this.stopTimeout): + glog.Warningf("Failed to stop sink: %s", sh.sink.Name()) + } + return + }(sh) + } +} + +func export(s core.EventSink, data *core.EventBatch) { + startTime := time.Now() + defer exporterDuration. + WithLabelValues(s.Name()). + Observe(float64(time.Since(startTime)) / float64(time.Microsecond)) + s.ExportEvents(data) +} diff --git a/vendor/k8s.io/heapster/events/sinks/manager_test.go b/vendor/k8s.io/heapster/events/sinks/manager_test.go new file mode 100644 index 0000000000..33cb2e8eb3 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sinks/manager_test.go @@ -0,0 +1,114 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sinks + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + kube_api "k8s.io/kubernetes/pkg/api" + + "k8s.io/heapster/events/core" + "k8s.io/heapster/events/util" +) + +func doThreeBatches(manager core.EventSink) time.Duration { + now := time.Now() + batch := core.EventBatch{ + Timestamp: now, + Events: []*kube_api.Event{}, + } + + manager.ExportEvents(&batch) + manager.ExportEvents(&batch) + manager.ExportEvents(&batch) + + elapsed := time.Now().Sub(now) + return elapsed +} + +func TestAllExportsInTime(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", time.Second) + sink2 := util.NewDummySink("s2", time.Second) + manager, _ := NewEventSinkManager([]core.EventSink{sink1, sink2}, timeout, timeout) + + elapsed := doThreeBatches(manager) + if elapsed > 2*timeout+2*time.Second { + t.Fatalf("3xExportEvents took too long: %s", elapsed) + } + + assert.Equal(t, 3, sink1.GetExportCount()) + assert.Equal(t, 3, sink2.GetExportCount()) +} + +func TestOneExportInTime(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", time.Second) + sink2 := util.NewDummySink("s2", 30*time.Second) + manager, _ := NewEventSinkManager([]core.EventSink{sink1, sink2}, timeout, timeout) + + elapsed := doThreeBatches(manager) + if elapsed > 2*timeout+2*time.Second { + t.Fatalf("3xExportEvents took too long: %s", elapsed) + } + if elapsed < 2*timeout-1*time.Second { + t.Fatalf("3xExportEvents took too short: %s", elapsed) + } + + assert.Equal(t, 3, sink1.GetExportCount()) + assert.Equal(t, 1, sink2.GetExportCount()) +} + +func TestNoExportInTime(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", 30*time.Second) + sink2 := util.NewDummySink("s2", 30*time.Second) + manager, _ := NewEventSinkManager([]core.EventSink{sink1, sink2}, timeout, timeout) + + elapsed := doThreeBatches(manager) + if elapsed > 2*timeout+2*time.Second { + t.Fatalf("3xExportEvents took too long: %s", elapsed) + } + if elapsed < 2*timeout-1*time.Second { + t.Fatalf("3xExportEvents took too short: %s", elapsed) + } + + assert.Equal(t, 1, sink1.GetExportCount()) + assert.Equal(t, 1, sink2.GetExportCount()) +} + +func TestStop(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", 30*time.Second) + sink2 := util.NewDummySink("s2", 30*time.Second) + manager, _ := NewEventSinkManager([]core.EventSink{sink1, sink2}, timeout, timeout) + + now := time.Now() + manager.Stop() + elapsed := time.Now().Sub(now) + if elapsed > time.Second { + t.Fatalf("stop too long: %s", elapsed) + } + time.Sleep(time.Second) + + assert.Equal(t, true, sink1.IsStopped()) + assert.Equal(t, true, sink2.IsStopped()) +} diff --git a/vendor/k8s.io/heapster/events/sources/factory.go b/vendor/k8s.io/heapster/events/sources/factory.go new file mode 100644 index 0000000000..9035aad192 --- /dev/null +++ b/vendor/k8s.io/heapster/events/sources/factory.go @@ -0,0 +1,58 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sources + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/heapster/common/flags" + "k8s.io/heapster/events/core" + kube "k8s.io/heapster/events/sources/kubernetes" +) + +type SourceFactory struct { +} + +func (this *SourceFactory) Build(uri flags.Uri) (core.EventSource, error) { + switch uri.Key { + case "kubernetes": + src, err := kube.NewKubernetesSource(&uri.Val) + return src, err + default: + return nil, fmt.Errorf("Source not recognized: %s", uri.Key) + } +} + +func (this *SourceFactory) BuildAll(uris flags.Uris) ([]core.EventSource, error) { + if len(uris) != 1 { + return nil, fmt.Errorf("Only one source is supported") + } + result := []core.EventSource{} + for _, uri := range uris { + source, err := this.Build(uri) + if err != nil { + glog.Errorf("Failed to create %s: %v", uri.Key, err) + } else { + result = append(result, source) + } + } + return result, nil +} + +func NewSourceFactory() *SourceFactory { + return &SourceFactory{} +} diff --git a/vendor/k8s.io/heapster/events/sources/kubernetes/kubernetes_source.go b/vendor/k8s.io/heapster/events/sources/kubernetes/kubernetes_source.go new file mode 100644 index 0000000000..0b3b656ebb --- /dev/null +++ b/vendor/k8s.io/heapster/events/sources/kubernetes/kubernetes_source.go @@ -0,0 +1,195 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubernetes + +import ( + "net/url" + "time" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" + + kubeconfig "k8s.io/heapster/common/kubernetes" + "k8s.io/heapster/events/core" + kubeapi "k8s.io/kubernetes/pkg/api" + kubeapiunv "k8s.io/kubernetes/pkg/api/unversioned" + kubeclient "k8s.io/kubernetes/pkg/client/unversioned" + kubefields "k8s.io/kubernetes/pkg/fields" + kubelabels "k8s.io/kubernetes/pkg/labels" + kubewatch "k8s.io/kubernetes/pkg/watch" +) + +const ( + // Number of object pointers. Big enough so it won't be hit anytime soon with resonable GetNewEvents frequency. + LocalEventsBufferSize = 100000 +) + +var ( + // Last time of event since unix epoch in seconds + lastEventTimestamp = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: "eventer", + Subsystem: "scraper", + Name: "last_time_seconds", + Help: "Last time of event since unix epoch in seconds.", + }) + totalEventsNum = prometheus.NewCounter( + prometheus.CounterOpts{ + Namespace: "eventer", + Subsystem: "scraper", + Name: "events_total_number", + Help: "The total number of events.", + }) + scrapEventsDuration = prometheus.NewSummary( + prometheus.SummaryOpts{ + Namespace: "eventer", + Subsystem: "scraper", + Name: "duration_microseconds", + Help: "Time spent scraping events in microseconds.", + }) +) + +func init() { + prometheus.MustRegister(lastEventTimestamp) + prometheus.MustRegister(totalEventsNum) + prometheus.MustRegister(scrapEventsDuration) +} + +// Implements core.EventSource interface. +type KubernetesEventSource struct { + // Large local buffer, periodically read. + localEventsBuffer chan *kubeapi.Event + + stopChannel chan struct{} + + eventClient kubeclient.EventInterface +} + +func (this *KubernetesEventSource) GetNewEvents() *core.EventBatch { + startTime := time.Now() + defer lastEventTimestamp.Set(float64(time.Now().Unix())) + defer scrapEventsDuration.Observe(float64(time.Since(startTime)) / float64(time.Microsecond)) + result := core.EventBatch{ + Timestamp: time.Now(), + Events: []*kubeapi.Event{}, + } + // Get all data from the buffer. +event_loop: + for { + select { + case event := <-this.localEventsBuffer: + result.Events = append(result.Events, event) + default: + break event_loop + } + } + + totalEventsNum.Add(float64(len(result.Events))) + return &result +} + +func (this *KubernetesEventSource) watch() { + // Outer loop, for reconnections. + for { + events, err := this.eventClient.List(kubeapi.ListOptions{ + LabelSelector: kubelabels.Everything(), + FieldSelector: kubefields.Everything(), + }) + if err != nil { + glog.Errorf("Failed to load events: %v", err) + time.Sleep(time.Second) + continue + } + // Do not write old events. + + resourceVersion := events.ResourceVersion + + watcher, err := this.eventClient.Watch( + kubeapi.ListOptions{ + LabelSelector: kubelabels.Everything(), + FieldSelector: kubefields.Everything(), + Watch: true, + ResourceVersion: resourceVersion}) + if err != nil { + glog.Errorf("Failed to start watch for new events: %v", err) + time.Sleep(time.Second) + continue + } + + watchChannel := watcher.ResultChan() + // Inner loop, for update processing. + inner_loop: + for { + select { + case watchUpdate, ok := <-watchChannel: + if !ok { + glog.Errorf("Event watch channel closed") + break inner_loop + } + + if watchUpdate.Type == kubewatch.Error { + if status, ok := watchUpdate.Object.(*kubeapiunv.Status); ok { + glog.Errorf("Error during watch: %#v", status) + break inner_loop + } + glog.Errorf("Received unexpected error: %#v", watchUpdate.Object) + break inner_loop + } + + if event, ok := watchUpdate.Object.(*kubeapi.Event); ok { + switch watchUpdate.Type { + case kubewatch.Added, kubewatch.Modified: + select { + case this.localEventsBuffer <- event: + // Ok, buffer not full. + default: + // Buffer full, need to drop the event. + glog.Errorf("Event buffer full, dropping event") + } + case kubewatch.Deleted: + // Deleted events are silently ignored. + default: + glog.Warningf("Unknown watchUpdate.Type: %#v", watchUpdate.Type) + } + } else { + glog.Errorf("Wrong object received: %v", watchUpdate) + } + + case <-this.stopChannel: + glog.Infof("Event watching stopped") + return + } + } + } +} + +func NewKubernetesSource(uri *url.URL) (*KubernetesEventSource, error) { + kubeConfig, err := kubeconfig.GetKubeClientConfig(uri) + if err != nil { + return nil, err + } + kubeClient, err := kubeclient.New(kubeConfig) + if err != nil { + return nil, err + } + eventClient := kubeClient.Events(kubeapi.NamespaceAll) + result := KubernetesEventSource{ + localEventsBuffer: make(chan *kubeapi.Event, LocalEventsBufferSize), + stopChannel: make(chan struct{}), + eventClient: eventClient, + } + go result.watch() + return &result, nil +} diff --git a/vendor/k8s.io/heapster/events/util/dummies.go b/vendor/k8s.io/heapster/events/util/dummies.go new file mode 100644 index 0000000000..8553c6f02a --- /dev/null +++ b/vendor/k8s.io/heapster/events/util/dummies.go @@ -0,0 +1,84 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "sync" + "time" + + "k8s.io/heapster/events/core" +) + +type DummySink struct { + name string + mutex sync.Mutex + exportCount int + stopped bool + latency time.Duration +} + +func (this *DummySink) Name() string { + return this.name +} +func (this *DummySink) ExportEvents(*core.EventBatch) { + this.mutex.Lock() + this.exportCount++ + this.mutex.Unlock() + + time.Sleep(this.latency) +} + +func (this *DummySink) Stop() { + this.mutex.Lock() + this.stopped = true + this.mutex.Unlock() + + time.Sleep(this.latency) +} + +func (this *DummySink) IsStopped() bool { + this.mutex.Lock() + defer this.mutex.Unlock() + return this.stopped +} + +func (this *DummySink) GetExportCount() int { + this.mutex.Lock() + defer this.mutex.Unlock() + return this.exportCount +} + +func NewDummySink(name string, latency time.Duration) *DummySink { + return &DummySink{ + name: name, + latency: latency, + exportCount: 0, + stopped: false, + } +} + +type DummyEventSource struct { + eventBatch *core.EventBatch +} + +func (this *DummyEventSource) GetNewEvents() *core.EventBatch { + return this.eventBatch +} + +func NewDummySource(eventBatch *core.EventBatch) *DummyEventSource { + return &DummyEventSource{ + eventBatch: eventBatch, + } +} diff --git a/vendor/k8s.io/heapster/grafana/Dockerfile b/vendor/k8s.io/heapster/grafana/Dockerfile new file mode 100644 index 0000000000..fef5ea7c8b --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/Dockerfile @@ -0,0 +1,14 @@ +# +# Stock Grafana + a few custom dashboards +# + +FROM grafana/grafana:2.6.0 + +RUN apt-get update && \ + apt-get install -y curl + +COPY dashboards /dashboards +COPY run.sh /run.sh + +EXPOSE 3000 +ENTRYPOINT /run.sh diff --git a/vendor/k8s.io/heapster/grafana/Makefile b/vendor/k8s.io/heapster/grafana/Makefile new file mode 100644 index 0000000000..34c12be47b --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/Makefile @@ -0,0 +1,13 @@ + +TAG = v2.6.0-2 +PREFIX = kubernetes + +all: container + +container: + docker build -t $(PREFIX)/heapster_grafana:$(TAG) . + +push: + docker push $(PREFIX)/grafana:$(TAG) + + diff --git a/vendor/k8s.io/heapster/grafana/README.md b/vendor/k8s.io/heapster/grafana/README.md new file mode 100644 index 0000000000..1eb7fe1795 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/README.md @@ -0,0 +1,17 @@ +# Grafana Image For Heapster/InfluxDB + +## What's In It +* Stock Grafana. +* Create a datasource for InfluxDB. +* Create a couple of dashboards during startup. These dashboards leverage templating and repeating of panels features in Grafana 2.0, to discover nodes, pods, and containers automatically. + +## How To Use It +* InfluxDB service URL can be passed in via the environment variable __INFLUXDB_SERVICE_URL__. +* If __INFLUXDB_SERVICE_URL__ isn't defined, it will discover and use the external service URL, if available. +* Otherwise, it will fall back to http://monitoring-influxdb:8086. + +## How To Build It + +cd $GOPATH/src/k8s.io/heapster/grafana + +make all diff --git a/vendor/k8s.io/heapster/grafana/RELEASES.md b/vendor/k8s.io/heapster/grafana/RELEASES.md new file mode 100644 index 0000000000..a636b4f2c1 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/RELEASES.md @@ -0,0 +1,20 @@ +# Release Notes for Grafana container. + +## 2.6.0-2 (29-02-2016) +- Handle new Influxdb format +- Updated dashboards + +## 2.6.0 (29-12-2015) +- Support Grafana 2.6.0. +- Improve default dashboards + - Accurate CPU metrics + - Cluster network graphs + - Fix data aggregation + +## 2.5.0 (12-11-2015) +- Support Grafana 2.5.0. + +## 2.1.0 (9-28-2015) +- Support Grafana 2.1.0. +- Auto populate pods and nodes using Grafana templates. + diff --git a/vendor/k8s.io/heapster/grafana/dashboards/cluster.json b/vendor/k8s.io/heapster/grafana/dashboards/cluster.json new file mode 100644 index 0000000000..a496d86fc0 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/dashboards/cluster.json @@ -0,0 +1,2465 @@ +{ + "dashboard": { + "id": null, + "title": "Cluster", + "originalTitle": "Cluster", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 3, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/usage_rate", + "query": "SELECT sum(\"value\") FROM \"cpu/usage_rate\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/limit", + "query": "SELECT sum(\"value\") FROM \"cpu/limit\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/request", + "query": "SELECT sum(\"value\") FROM \"cpu/request\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster CPU Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + } + ], + "title": "Row" + }, + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 4, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/usage_rate", + "query": "SELECT sum(\"value\") FROM \"cpu/usage_rate\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/limit", + "query": "SELECT sum(\"value\") FROM \"cpu/limit\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/request", + "query": "SELECT sum(\"value\") FROM \"cpu/request\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 6, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/usage_rate", + "query": "SELECT sum(\"value\") FROM \"cpu/usage_rate\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/limit", + "query": "SELECT sum(\"value\") FROM \"cpu/limit\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/request", + "query": "SELECT sum(\"value\") FROM \"cpu/request\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual CPU Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + } + ], + "title": "New row" + }, + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 1, + "isNew": true, + "leftYAxisLabel": "bytes", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/usage", + "query": "SELECT sum(\"value\") FROM \"memory/usage\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Working Set", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/working_set", + "query": "SELECT sum(\"value\") FROM \"memory/working_set\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "memory/limit", + "query": "SELECT sum(\"value\") FROM \"memory/limit\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "memory/request", + "query": "SELECT sum(\"value\") FROM \"memory/request\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster Memory Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 2, + "isNew": true, + "leftYAxisLabel": "Bytes", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/usage", + "query": "SELECT sum(\"value\") FROM \"memory/usage\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/working_set", + "query": "SELECT sum(\"value\") FROM \"memory/working_set\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/limit", + "query": "SELECT sum(\"value\") FROM \"memory/limit\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/request", + "query": "SELECT sum(\"value\") FROM \"memory/request\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + } + ], + "title": "New row" + }, + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 5, + "isNew": true, + "leftYAxisLabel": "Bytes", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/usage", + "query": "SELECT sum(\"value\") FROM \"memory/usage\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/working_set", + "query": "SELECT sum(\"value\") FROM \"memory/working_set\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/limit", + "query": "SELECT sum(\"value\") FROM \"memory/limit\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/request", + "query": "SELECT sum(\"value\") FROM \"memory/request\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Node Memory Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 7, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/tx_rate", + "query": "SELECT sum(\"value\") FROM \"network/tx_rate\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Rx", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/rx_rate", + "query": "SELECT sum(\"value\") FROM \"network/rx_rate\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster Network Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 8, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/tx_rate", + "query": "SELECT sum(\"value\") FROM \"network/tx_rate\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Rx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/rx_rate", + "query": "SELECT sum(\"value\") FROM \"network/rx_rate\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Network Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 9, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/tx_rate", + "query": "SELECT sum(\"value\") FROM \"network/tx_rate\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Rx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/rx_rate", + "query": "SELECT sum(\"value\") FROM \"network/rx_rate\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Node Network Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 10, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/usage", + "query": "SELECT sum(\"value\") FROM \"filesystem/usage\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/limit", + "query": "SELECT sum(\"value\") FROM \"filesystem/limit\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster Filesystem Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 11, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/usage", + "query": "SELECT sum(\"value\") FROM \"filesystem/usage\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/limit", + "query": "SELECT sum(\"value\") FROM \"filesystem/limit\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Filesystem Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 12, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/usage", + "query": "SELECT sum(\"value\") FROM \"filesystem/usage\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/limit", + "query": "SELECT sum(\"value\") FROM \"filesystem/limit\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Filesystem Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + } + ], + "title": "New row" + } + ], + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [ + { + "allFormat": "glob", + "current": { + "text": "kubernetes-minion-hly9", + "value": "kubernetes-minion-hly9" + }, + "datasource": null, + "includeAll": false, + "multi": false, + "multiFormat": "glob", + "name": "nodename", + "options": [ + { + "text": "kubernetes-minion-a7kc", + "value": "kubernetes-minion-a7kc", + "selected": false + }, + { + "text": "kubernetes-minion-hly9", + "value": "kubernetes-minion-hly9", + "selected": true + }, + { + "text": "kubernetes-minion-sfos", + "value": "kubernetes-minion-sfos", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"uptime\" WITH KEY = \"nodename\"", + "refresh": true, + "regex": "", + "type": "query" + } + ] + }, + "annotations": { + "list": [] + }, + "refresh": false, + "schemaVersion": 8, + "version": 10, + "links": [] + }, + "overwrite": false +} \ No newline at end of file diff --git a/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/README.md b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/README.md new file mode 100644 index 0000000000..86ab4f97f6 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/README.md @@ -0,0 +1,12 @@ +# Grafana Dashboards using InfluxDB fields + +If you're using `withfields=true` parameters in InfluxDB sink URL, +the storage schema changes in InfluxDB. +So you need to use those Grafana Dashboards: +* If you're using heapster: + * pods.json + * cluster.json +* If you're using eventer: + * events.json + +More info [here](/docs/storage-schema.md) diff --git a/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/cluster.json b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/cluster.json new file mode 100644 index 0000000000..20abdcebfd --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/cluster.json @@ -0,0 +1,2465 @@ +{ + "dashboard": { + "id": null, + "title": "Cluster", + "originalTitle": "Cluster", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 3, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"usage_rate\") FROM \"cpu\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"limit\") FROM \"cpu\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"request\") FROM \"cpu\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster CPU Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + } + ], + "title": "Row" + }, + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 4, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"usage_rate\") FROM \"cpu\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"limit\") FROM \"cpu\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"request\") FROM \"cpu\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 6, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"usage_rate\") FROM \"cpu\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"limite\") FROM \"cpu\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"request\") FROM \"cpu\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual CPU Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + } + ], + "title": "New row" + }, + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 1, + "isNew": true, + "leftYAxisLabel": "bytes", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"usage\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Working Set", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"working_set\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "working_set" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "memory", + "query": "SELECT sum(\"limit\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "memory", + "query": "SELECT sum(\"request\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster Memory Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 2, + "isNew": true, + "leftYAxisLabel": "Bytes", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"usage\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"working_set\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "working_set" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"limit\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"request\") FROM \"memory\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + } + ], + "title": "New row" + }, + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 5, + "isNew": true, + "leftYAxisLabel": "Bytes", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"usage\") FROM \"memory\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"working_set\") FROM \"memory\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "working_set" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"limit\") FROM \"memory\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Request $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"request\") FROM \"memory\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Node Memory Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 7, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"tx_rate\") FROM \"network\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "tx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Rx", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"rx_rate\") FROM \"network\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "rx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster Network Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 8, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"tx_rate\") FROM \"network\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "tx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Rx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"rx_rate\") FROM \"network\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "rx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Network Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 9, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"tx_rate\") FROM \"network\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "tx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Rx $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"rx_rate\") FROM \"network\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "rx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Node Network Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 10, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"usage\") FROM \"filesystem\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"limit\") FROM \"filesystem\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval) fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Overal Cluster Filesystem Usage", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 11, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"usage\") FROM \"filesystem\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"limit\") FROM \"filesystem\" WHERE \"type\" = 'node' AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Filesystem Usage by Node", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 12, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"usage\") FROM \"filesystem\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + }, + { + "alias": "Limit $tag_nodename", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "nodename" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"limit\") FROM \"filesystem\" WHERE \"type\" = 'node' AND \"nodename\" =~ /$nodename$/ AND $timeFilter GROUP BY time($interval), \"nodename\" fill(null)", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "node" + }, + { + "condition": "AND", + "key": "nodename", + "operator": "=~", + "value": "/$nodename$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Filesystem Usage: $nodename", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + } + ], + "title": "New row" + } + ], + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [ + { + "allFormat": "glob", + "current": { + "text": "kubernetes-minion-hly9", + "value": "kubernetes-minion-hly9" + }, + "datasource": null, + "includeAll": false, + "multi": false, + "multiFormat": "glob", + "name": "nodename", + "options": [ + { + "text": "kubernetes-minion-a7kc", + "value": "kubernetes-minion-a7kc", + "selected": false + }, + { + "text": "kubernetes-minion-hly9", + "value": "kubernetes-minion-hly9", + "selected": true + }, + { + "text": "kubernetes-minion-sfos", + "value": "kubernetes-minion-sfos", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"uptime\" WITH KEY = \"nodename\"", + "refresh": true, + "regex": "", + "type": "query" + } + ] + }, + "annotations": { + "list": [] + }, + "refresh": false, + "schemaVersion": 8, + "version": 10, + "links": [] + }, + "overwrite": false +} diff --git a/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/events.json b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/events.json new file mode 100644 index 0000000000..98e7800ef3 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/events.json @@ -0,0 +1,389 @@ +{ + "id": 8, + "title": "Events", + "originalTitle": "Events", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": true, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 3, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "events.count" + } + ], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "events", + "query": "SELECT count(message) FROM \"events\" WHERE \"namespace_name\" =~ /$namespace$/ AND \"kind\" =~ /$kind$/ AND \"object_name\" =~ /$object_name$/ AND \"type\" =~ /$type$/ AND \"reason\" =~ /$reason$/ AND $timeFilter GROUP BY time(1m) fill(null)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "message" + ], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Events by minutes", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + } + ], + "title": "Events by minute" + }, + { + "collapse": false, + "editable": true, + "height": "25px", + "panels": [ + { + "columns": [], + "editable": true, + "error": false, + "fontSize": "90%", + "id": 4, + "isNew": true, + "links": [], + "pageSize": 25, + "scroll": false, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "span": 12, + "styles": [ + { + "dateFormat": "YYYY/MM/DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "string", + "unit": "short" + } + ], + "targets": [ + { + "alias": "", + "dsType": "influxdb", + "groupBy": [], + "hide": false, + "measurement": "events", + "query": "SELECT \"namespace_name\" as \"Namespace\", \"hostname\" as \"Node_name\", \"kind\" as \"Object_kind\", \"object_name\" as \"Object_Name\", \"type\" as \"Type\", \"reason\" as \"Reason\", \"message\" as \"Message\" FROM \"events\" WHERE \"namespace_name\" =~ /$namespace$/ AND \"kind\" =~ /$kind$/ AND \"object_name\" =~ /$object_name$/ AND \"type\" =~ /$type$/ AND \"reason\" =~ /$reason$/ AND $timeFilter", + "rawQuery": true, + "refId": "A", + "resultFormat": "table", + "select": [ + [ + { + "params": [ + "message" + ], + "type": "field" + } + ] + ], + "tags": [] + } + ], + "title": "Events", + "transform": "table", + "type": "table" + } + ], + "title": "Event list" + } + ], + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [ + { + "allFormat": "regex wildcard", + "current": { + "text": "All", + "value": ".*" + }, + "datasource": null, + "includeAll": true, + "label": "Namespace", + "multi": true, + "multiFormat": "pipe", + "name": "namespace", + "options": [ + { + "text": "All", + "value": ".*", + "selected": true + } + ], + "query": "SHOW TAG VALUES FROM \"events\" WITH KEY = \"namespace_name\"", + "refresh": true, + "type": "query" + }, + { + "allFormat": "regex wildcard", + "current": { + "text": "All", + "value": ".*" + }, + "datasource": null, + "includeAll": true, + "label": "Kind", + "multi": true, + "multiFormat": "pipe", + "name": "kind", + "options": [ + { + "text": "All", + "value": ".*", + "selected": true + }, + { + "text": "Pod", + "value": "Pod", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"events\" WITH KEY = \"kind\" WHERE \"namespace_name\" =~ /$namespace$/ ", + "refresh": true, + "type": "query" + }, + { + "allFormat": "regex wildcard", + "current": { + "text": "All", + "value": ".*" + }, + "datasource": null, + "includeAll": true, + "label": "Object Name", + "multi": true, + "multiFormat": "pipe", + "name": "object_name", + "options": [ + { + "text": "All", + "value": ".*", + "selected": true + } + ], + "query": "SHOW TAG VALUES FROM \"events\" WITH KEY = \"object_name\" WHERE \"kind\" =~ /$kind$/ AND \"namespace_name\" =~ /$namespace$/ ", + "refresh": true, + "regex": "", + "type": "query" + }, + { + "allFormat": "regex wildcard", + "current": { + "text": "All", + "value": ".*" + }, + "datasource": null, + "includeAll": true, + "label": "Severity", + "multi": true, + "multiFormat": "pipe", + "name": "type", + "options": [ + { + "text": "All", + "value": ".*", + "selected": true + }, + { + "text": "Normal", + "value": "Normal", + "selected": false + }, + { + "text": "Warning", + "value": "Warning", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"events\" WITH KEY = \"type\"", + "refresh": true, + "regex": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "regex wildcard", + "current": { + "text": "All", + "value": ".*" + }, + "datasource": null, + "includeAll": true, + "label": "Reason", + "multi": true, + "multiFormat": "pipe", + "name": "reason", + "options": [ + { + "text": "All", + "value": ".*", + "selected": true + }, + { + "text": "BackOff", + "value": "BackOff", + "selected": false + }, + { + "text": "Failed", + "value": "Failed", + "selected": false + }, + { + "text": "FailedSync", + "value": "FailedSync", + "selected": false + }, + { + "text": "Pulling", + "value": "Pulling", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"events\" WITH KEY = \"reason\"", + "refresh": true, + "regex": "", + "type": "query", + "useTags": false + } + ] + }, + "annotations": { + "list": [] + }, + "refresh": "5s", + "schemaVersion": 8, + "version": 18, + "links": [] +} diff --git a/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/pods.json b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/pods.json new file mode 100644 index 0000000000..ce6ce3b69e --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/dashboards/influxdb_withfields/pods.json @@ -0,0 +1,1060 @@ +{ + "dashboard": { + "id": null, + "title": "Pods", + "originalTitle": "Pods", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 6, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "type": "tag", + "params": [ + "container_name" + ] + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"usage_rate\") FROM \"cpu\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Limit $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "type": "tag", + "params": [ + "container_name" + ] + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"limit\") FROM \"cpu\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Request $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "type": "tag", + "params": [ + "container_name" + ] + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu", + "query": "SELECT sum(\"request\") FROM \"cpu\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual CPU Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 7, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"usage\") FROM \"memory\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Limit $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"limit\") FROM \"memory\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Request $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"request\") FROM \"memory\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "request" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Working Set $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory", + "query": "SELECT sum(\"working_set\") FROM \"memory\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "working_set" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Memory Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 8, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"tx_rate\") FROM \"network\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "tx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Rx $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network", + "query": "SELECT sum(\"rx_rate\") FROM \"network\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "rx_rate" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Network Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 9, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"usage\") FROM \"filesystem\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "usage" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Limit $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem", + "query": "SELECT sum(\"limit\") FROM \"filesystem\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "limit" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Filesystem Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + } + ], + "title": "Row" + } + ], + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [ + { + "allFormat": "glob", + "current": { + "tags": [], + "text": "kube-system", + "value": "kube-system" + }, + "datasource": null, + "includeAll": false, + "multi": false, + "multiFormat": "glob", + "name": "namespace", + "options": [ + { + "selected": false, + "text": "default", + "value": "default" + }, + { + "selected": true, + "text": "kube-system", + "value": "kube-system" + } + ], + "query": "SHOW TAG VALUES FROM \"uptime\" WITH KEY = \"namespace_name\"", + "refresh": false, + "type": "query" + }, + { + "allFormat": "glob", + "current": { + "text": "heapster-v19-33x3r", + "value": "heapster-v19-33x3r", + "tags": [] + }, + "datasource": null, + "includeAll": false, + "multi": false, + "multiFormat": "glob", + "name": "podname", + "options": [ + { + "text": "fluentd-cloud-logging-kubernetes-minion-a7kc", + "value": "fluentd-cloud-logging-kubernetes-minion-a7kc", + "selected": false + }, + { + "text": "fluentd-cloud-logging-kubernetes-minion-hly9", + "value": "fluentd-cloud-logging-kubernetes-minion-hly9", + "selected": false + }, + { + "text": "fluentd-cloud-logging-kubernetes-minion-sfos", + "value": "fluentd-cloud-logging-kubernetes-minion-sfos", + "selected": false + }, + { + "text": "heapster-v19-33x3r", + "value": "heapster-v19-33x3r", + "selected": true + }, + { + "text": "kube-dns-v10-v2yty", + "value": "kube-dns-v10-v2yty", + "selected": false + }, + { + "text": "kubernetes-dashboard-gqcgk", + "value": "kubernetes-dashboard-gqcgk", + "selected": false + }, + { + "text": "kubernetes-dashboard-v0.1.0-jz8z8", + "value": "kubernetes-dashboard-v0.1.0-jz8z8", + "selected": false + }, + { + "text": "l7-lb-controller-eojgv", + "value": "l7-lb-controller-eojgv", + "selected": false + }, + { + "text": "monitoring-influxdb-grafana-v6-qxucl", + "value": "monitoring-influxdb-grafana-v6-qxucl", + "selected": false + }, + { + "text": "php-apache-m19ce", + "value": "php-apache-m19ce", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"uptime\" WITH KEY = \"pod_name\"", + "refresh": true, + "regex": "", + "type": "query" + } + ] + }, + "annotations": { + "list": [] + }, + "refresh": false, + "schemaVersion": 8, + "version": 14, + "links": [] + }, + "overwrite": false +} diff --git a/vendor/k8s.io/heapster/grafana/dashboards/pods.json b/vendor/k8s.io/heapster/grafana/dashboards/pods.json new file mode 100644 index 0000000000..2aa80eb109 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/dashboards/pods.json @@ -0,0 +1,1060 @@ +{ + "dashboard": { + "id": null, + "title": "Pods", + "originalTitle": "Pods", + "tags": [], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 6, + "isNew": true, + "leftYAxisLabel": "Millicores", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "type": "tag", + "params": [ + "container_name" + ] + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/usage_rate", + "query": "SELECT sum(\"value\") FROM \"cpu/usage_rate\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Limit $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "type": "tag", + "params": [ + "container_name" + ] + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/limit", + "query": "SELECT sum(\"value\") FROM \"cpu/limit\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Request $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "type": "tag", + "params": [ + "container_name" + ] + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "cpu/request", + "query": "SELECT sum(\"value\") FROM \"cpu/request\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual CPU Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "short", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 7, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/usage", + "query": "SELECT sum(\"value\") FROM \"memory/usage\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Limit $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/limit", + "query": "SELECT sum(\"value\") FROM \"memory/limit\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Request $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/request", + "query": "SELECT sum(\"value\") FROM \"memory/request\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Working Set $namespace $podname $tag_container_name", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "container_name" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "memory/working_set", + "query": "SELECT sum(\"value\") FROM \"memory/working_set\" WHERE \"type\" = 'pod_container' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval), \"container_name\" fill(null)", + "rawQuery": false, + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod_container" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Memory Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 8, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Tx $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/tx_rate", + "query": "SELECT sum(\"value\") FROM \"network/tx_rate\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Rx $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "network/rx_rate", + "query": "SELECT sum(\"value\") FROM \"network/rx_rate\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Individual Network Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "Bps", + "short" + ] + }, + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null, + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 9, + "isNew": true, + "leftYAxisLabel": "", + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Usage $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/usage", + "query": "SELECT sum(\"value\") FROM \"filesystem/usage\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + }, + { + "alias": "Limit $namespace $podname", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "filesystem/limit", + "query": "SELECT sum(\"value\") FROM \"filesystem/limit\" WHERE \"type\" = 'pod' AND \"namespace_name\" =~ /$namespace$/ AND \"pod_name\" =~ /$podname$/ AND $timeFilter GROUP BY time($interval) fill(null)", + "rawQuery": false, + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "sum" + } + ] + ], + "tags": [ + { + "key": "type", + "operator": "=", + "value": "pod" + }, + { + "condition": "AND", + "key": "namespace_name", + "operator": "=~", + "value": "/$namespace$/" + }, + { + "condition": "AND", + "key": "pod_name", + "operator": "=~", + "value": "/$podname$/" + } + ] + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Filesystem Usage: $namespace $podname", + "tooltip": { + "shared": true, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "y-axis": true, + "y_formats": [ + "bytes", + "short" + ] + } + ], + "title": "Row" + } + ], + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [ + { + "allFormat": "glob", + "current": { + "tags": [], + "text": "kube-system", + "value": "kube-system" + }, + "datasource": null, + "includeAll": false, + "multi": false, + "multiFormat": "glob", + "name": "namespace", + "options": [ + { + "selected": false, + "text": "default", + "value": "default" + }, + { + "selected": true, + "text": "kube-system", + "value": "kube-system" + } + ], + "query": "SHOW TAG VALUES FROM \"uptime\" WITH KEY = \"namespace_name\"", + "refresh": false, + "type": "query" + }, + { + "allFormat": "glob", + "current": { + "text": "heapster-v19-33x3r", + "value": "heapster-v19-33x3r", + "tags": [] + }, + "datasource": null, + "includeAll": false, + "multi": false, + "multiFormat": "glob", + "name": "podname", + "options": [ + { + "text": "fluentd-cloud-logging-kubernetes-minion-a7kc", + "value": "fluentd-cloud-logging-kubernetes-minion-a7kc", + "selected": false + }, + { + "text": "fluentd-cloud-logging-kubernetes-minion-hly9", + "value": "fluentd-cloud-logging-kubernetes-minion-hly9", + "selected": false + }, + { + "text": "fluentd-cloud-logging-kubernetes-minion-sfos", + "value": "fluentd-cloud-logging-kubernetes-minion-sfos", + "selected": false + }, + { + "text": "heapster-v19-33x3r", + "value": "heapster-v19-33x3r", + "selected": true + }, + { + "text": "kube-dns-v10-v2yty", + "value": "kube-dns-v10-v2yty", + "selected": false + }, + { + "text": "kubernetes-dashboard-gqcgk", + "value": "kubernetes-dashboard-gqcgk", + "selected": false + }, + { + "text": "kubernetes-dashboard-v0.1.0-jz8z8", + "value": "kubernetes-dashboard-v0.1.0-jz8z8", + "selected": false + }, + { + "text": "l7-lb-controller-eojgv", + "value": "l7-lb-controller-eojgv", + "selected": false + }, + { + "text": "monitoring-influxdb-grafana-v6-qxucl", + "value": "monitoring-influxdb-grafana-v6-qxucl", + "selected": false + }, + { + "text": "php-apache-m19ce", + "value": "php-apache-m19ce", + "selected": false + } + ], + "query": "SHOW TAG VALUES FROM \"uptime\" WITH KEY = \"pod_name\"", + "refresh": true, + "regex": "", + "type": "query" + } + ] + }, + "annotations": { + "list": [] + }, + "refresh": false, + "schemaVersion": 8, + "version": 14, + "links": [] + }, + "overwrite": false +} \ No newline at end of file diff --git a/vendor/k8s.io/heapster/grafana/run.sh b/vendor/k8s.io/heapster/grafana/run.sh new file mode 100755 index 0000000000..11d2d19ff9 --- /dev/null +++ b/vendor/k8s.io/heapster/grafana/run.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +HEADER_CONTENT_TYPE="Content-Type: application/json" +HEADER_ACCEPT="Accept: application/json" + +GRAFANA_USER=${GRAFANA_USER:-admin} +GRAFANA_PASSWD=${GRAFANA_PASSWD:-admin} +GRAFANA_PORT=${GRAFANA_PORT:-3000} + +INFLUXDB_HOST=${INFLUXDB_HOST:-"monitoring-influxdb"} +INFLUXDB_DATABASE=${INFLUXDB_DATABASE:-k8s} +INFLUXDB_PASSWORD=${INFLUXDB_PASSWORD:-root} +INFLUXDB_PORT=${INFLUXDB_PORT:-8086} +INFLUXDB_USER=${INFLUXDB_USER:-root} + +DASHBOARD_LOCATION=${DASHBOARD_LOCATION:-"/dashboards"} + +# Allow access to dashboards without having to log in +export GF_AUTH_ANONYMOUS_ENABLED=${GF_AUTH_ANONYMOUS_ENABLED:-true} +export GF_SERVER_HTTP_PORT=${GRAFANA_PORT} + +GF_SERVER_PROTOCOL=${GF_SERVER_PROTOCOL:-http} + +BACKEND_ACCESS_MODE=${BACKEND_ACCESS_MODE:-proxy} +INFLUXDB_SERVICE_URL=${INFLUXDB_SERVICE_URL} +if [ -n "$INFLUXDB_SERVICE_URL" ]; then + echo "Influxdb service URL is provided." +else + INFLUXDB_SERVICE_URL="http://${INFLUXDB_HOST}:${INFLUXDB_PORT}" +fi + +echo "Using the following URL for InfluxDB: ${INFLUXDB_SERVICE_URL}" +echo "Using the following backend access mode for InfluxDB: ${BACKEND_ACCESS_MODE}" + +set -m +echo "Starting Grafana in the background" +exec /usr/sbin/grafana-server --homepath=/usr/share/grafana --config=/etc/grafana/grafana.ini cfg:default.paths.data=/var/lib/grafana cfg:default.paths.logs=/var/log/grafana & + +echo "Waiting for Grafana to come up..." +until $(curl -k --fail --output /dev/null --silent ${GF_SERVER_PROTOCOL}://${GRAFANA_USER}:${GRAFANA_PASSWD}@localhost:${GRAFANA_PORT}/api/org); do + printf "." + sleep 2 +done +echo "Grafana is up and running." +echo "Creating default influxdb datasource..." +curl -k -i -XPOST -H "${HEADER_ACCEPT}" -H "${HEADER_CONTENT_TYPE}" "${GF_SERVER_PROTOCOL}://${GRAFANA_USER}:${GRAFANA_PASSWD}@localhost:${GRAFANA_PORT}/api/datasources" -d ' +{ + "name": "influxdb-datasource", + "type": "influxdb", + "access": "'"${BACKEND_ACCESS_MODE}"'", + "isDefault": true, + "url": "'"${INFLUXDB_SERVICE_URL}"'", + "password": "'"${INFLUXDB_PASSWORD}"'", + "user": "'"${INFLUXDB_USER}"'", + "database": "'"${INFLUXDB_DATABASE}"'" +}' + +echo "" +echo "Importing default dashboards..." +for filename in ${DASHBOARD_LOCATION}/*.json; do + echo "Importing ${filename} ..." + curl -k -i -XPOST --data "@${filename}" -H "${HEADER_ACCEPT}" -H "${HEADER_CONTENT_TYPE}" "${GF_SERVER_PROTOCOL}://${GRAFANA_USER}:${GRAFANA_PASSWD}@localhost:${GRAFANA_PORT}/api/dashboards/db" + echo "" + echo "Done importing ${filename}" +done +echo "" +echo "Bringing Grafana back to the foreground" +fg + diff --git a/vendor/k8s.io/heapster/hooks/boilerplate.go.txt b/vendor/k8s.io/heapster/hooks/boilerplate.go.txt new file mode 100644 index 0000000000..5804195f2d --- /dev/null +++ b/vendor/k8s.io/heapster/hooks/boilerplate.go.txt @@ -0,0 +1,13 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. diff --git a/vendor/k8s.io/heapster/hooks/check_boilerplate.sh b/vendor/k8s.io/heapster/hooks/check_boilerplate.sh new file mode 100755 index 0000000000..bd5eec193c --- /dev/null +++ b/vendor/k8s.io/heapster/hooks/check_boilerplate.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +REF_FILE="./hooks/boilerplate.go.txt" +if [ ! -e $REF_FILE ]; then + echo "Missing reference file: " ${REF_FILE} + exit 1 +fi + +LINES=$(cat "${REF_FILE}" | wc -l | tr -d ' ') +GO_FILES=$(find . -name "*.go" | grep -v -e "vendor") + +for FILE in ${GO_FILES}; do + DIFFER=$(cat "${FILE}" | sed 's/2015/2014/g;s/2016/2014/g' | head "-${LINES}" | diff -q - "${REF_FILE}") + + if [[ ! -z "${DIFFER}" ]]; then + echo "${FILE} does not have the correct copyright notice." + exit 1 + fi +done diff --git a/vendor/k8s.io/heapster/hooks/check_gofmt.sh b/vendor/k8s.io/heapster/hooks/check_gofmt.sh new file mode 100755 index 0000000000..fda1be44c1 --- /dev/null +++ b/vendor/k8s.io/heapster/hooks/check_gofmt.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FILES=$(gofmt -l `find . -type f -name "*.go" | grep -v vendor`) +if [[ ! -z "$FILES" ]]; then + echo Run gofmt on the following files:$'\n' $FILES + exit 1 +fi diff --git a/vendor/k8s.io/heapster/hooks/coverage.sh b/vendor/k8s.io/heapster/hooks/coverage.sh new file mode 100755 index 0000000000..0073b5c5b0 --- /dev/null +++ b/vendor/k8s.io/heapster/hooks/coverage.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# Generate test coverage statistics for Go packages. +# +# Works around the fact that `go test -coverprofile` currently does not work +# with multiple packages, see https://code.google.com/p/go/issues/detail?id=6909 +# +# Usage: script/coverage [--html|--coveralls] +# +# --html Additionally create HTML report and open it in browser +# --coveralls Push coverage statistics to coveralls.io +# + +set -e + +workdir=.cover +profile="$workdir/cover.out" +mode=count + +generate_cover_data() { + rm -rf "$workdir" + mkdir "$workdir" + + for pkg in "$@"; do + f="$workdir/$(echo $pkg | tr / -).cover" + godep go test -test.short -covermode="$mode" -coverprofile="$f" "$pkg" + done + + echo "mode: $mode" >"$profile" + grep -h -v "^mode:" "$workdir"/*.cover >>"$profile" +} + +show_cover_report() { + godep go tool cover -${1}="$profile" +} + +push_to_coveralls() { + echo "Pushing coverage statistics to coveralls.io" + goveralls -coverprofile="$profile" -service=travis-ci -repotoken $COVERALLS_TOKEN +} + +generate_cover_data $(godep go list ./...) +show_cover_report func +case "$1" in +"") + ;; +--html) + show_cover_report html ;; +--coveralls) + push_to_coveralls ;; +*) + echo >&2 "error: invalid option: $1"; exit 1 ;; +esac diff --git a/vendor/k8s.io/heapster/hooks/run_vet.sh b/vendor/k8s.io/heapster/hooks/run_vet.sh new file mode 100755 index 0000000000..03e4b3c7dc --- /dev/null +++ b/vendor/k8s.io/heapster/hooks/run_vet.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o pipefail + +# Filter silly "exit status 1" lines and send main output to stdout. +# +# This is tricky - pipefail means any non-zero exit in a pipeline is reported, +# and errexit exits on error. Turning that into an || expression blocks the +# errexit. But $? is still not useful because grep will return an error when it +# receives no input, which is exactly what go vet produces on success. In +# short, if go vet fails (produces output), grep will succeed, but if go vet +# succeeds (produces no output) grep will fail. Then we just look at +# PIPESTATUS[0] which is go's exit code. +rc=0 +go vet $(go list ./... | grep -v /vendor/) 2>&1 | grep -v "^exit status " || rc=${PIPESTATUS[0]} +exit "${rc}" diff --git a/vendor/k8s.io/heapster/influxdb/Dockerfile b/vendor/k8s.io/heapster/influxdb/Dockerfile new file mode 100644 index 0000000000..0d2d81cb37 --- /dev/null +++ b/vendor/k8s.io/heapster/influxdb/Dockerfile @@ -0,0 +1,21 @@ +FROM ubuntu + +MAINTAINER Vishnu kannan "" + +# Install InfluxDB +ENV INFLUXDB_VERSION 0.12.2-1 + +RUN apt-get update && apt-get install -y curl && mkdir /app && curl -s -o /app/influxdb_latest_amd64.deb https://s3.amazonaws.com/influxdb/influxdb_${INFLUXDB_VERSION}_amd64.deb && \ + dpkg -i /app/influxdb_latest_amd64.deb && \ + rm /app/influxdb_latest_amd64.deb + +ENV PATH=/opt/influxdb:$PATH + +ADD config.toml /etc/influxdb.toml + +# admin, http, udp, cluster, graphite, opentsdb, collectd +EXPOSE 8083 8086 8086/udp 8088 2003 4242 25826 + +VOLUME ["/data"] + +ENTRYPOINT ["influxd", "--config", "/etc/influxdb.toml"] \ No newline at end of file diff --git a/vendor/k8s.io/heapster/influxdb/RELEASES.md b/vendor/k8s.io/heapster/influxdb/RELEASES.md new file mode 100644 index 0000000000..c60547034d --- /dev/null +++ b/vendor/k8s.io/heapster/influxdb/RELEASES.md @@ -0,0 +1,16 @@ +# Release Notes for heapster influxdb container. + +## 0.7 (06-27-2016) +- updated to v0.12.2-1 + +## 0.6 (12-10-2015) +- updated to v0.9.6 + +## 0.5 (9-18-2015) +- updated to v0.9.4 + +## 0.4 (9-17-2015) +- Updated to InfluxDB v0.8.9 to pave way for safely upgrading to influxDB v0.9.x + +## 0.3 (1-19-2015) +- Updated Influxdb version number to 0.8.8, paving way for collectd support. diff --git a/vendor/k8s.io/heapster/influxdb/build.sh b/vendor/k8s.io/heapster/influxdb/build.sh new file mode 100755 index 0000000000..1cddcb9adc --- /dev/null +++ b/vendor/k8s.io/heapster/influxdb/build.sh @@ -0,0 +1,5 @@ +#! /bin/bash + +pushd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +docker build -t heapster_influxdb:canary . +popd diff --git a/vendor/k8s.io/heapster/influxdb/config.toml b/vendor/k8s.io/heapster/influxdb/config.toml new file mode 100644 index 0000000000..7b34c47262 --- /dev/null +++ b/vendor/k8s.io/heapster/influxdb/config.toml @@ -0,0 +1,100 @@ +reporting-disabled = false + +[meta] + dir = "/data/meta" + hostname = "localhost" + bind-address = ":8088" + retention-autocreate = true + election-timeout = "1s" + heartbeat-timeout = "1s" + leader-lease-timeout = "500ms" + commit-timeout = "50ms" + cluster-tracing = false + +[data] + dir = "/data/data" + max-wal-size = 104857600 + wal-flush-interval = "10m0s" + wal-partition-flush-delay = "2s" + wal-dir = "/data/wal" + wal-logging-enabled = true + wal-ready-series-size = 30720 + wal-compaction-threshold = 0.5 + wal-max-series-size = 1048576 + wal-flush-cold-interval = "5s" + wal-partition-size-threshold = 20971520 + +[cluster] + force-remote-mapping = false + write-timeout = "5s" + shard-writer-timeout = "5s" + shard-mapper-timeout = "5s" + +[retention] + enabled = true + check-interval = "30m0s" + +[shard-precreation] + enabled = true + check-interval = "10m0s" + advance-period = "30m0s" + +[admin] + enabled = true + bind-address = ":8083" + https-enabled = false + https-certificate = "/etc/ssl/influxdb.pem" + +[monitor] + store-enabled = true + store-database = "_internal" + store-interval = "10s" + +[http] + enabled = true + bind-address = ":8086" + auth-enabled = false + log-enabled = true + write-tracing = false + pprof-enabled = false + https-enabled = false + https-certificate = "/etc/ssl/influxdb.pem" + +[collectd] + enabled = false + bind-address = ":25826" + database = "collectd" + retention-policy = "" + batch-size = 1000 + batch-pending = 5 + batch-timeout = "10s" + typesdb = "/usr/share/collectd/types.db" + +[opentsdb] + enabled = false + bind-address = ":4242" + database = "opentsdb" + retention-policy = "" + consistency-level = "one" + tls-enabled = false + certificate = "/etc/ssl/influxdb.pem" + batch-size = 1000 + batch-pending = 5 + batch-timeout = "1s" + +[continuous_queries] + log-enabled = true + enabled = true + recompute-previous-n = 2 + recompute-no-older-than = "10m0s" + compute-runs-per-interval = 10 + compute-no-more-than = "2m0s" + +[hinted-handoff] + enabled = true + dir = "/data/hh" + max-size = 1073741824 + max-age = "168h0m0s" + retry-rate-limit = 0 + retry-interval = "1s" + diff --git a/vendor/k8s.io/heapster/influxdb/start b/vendor/k8s.io/heapster/influxdb/start new file mode 100755 index 0000000000..78013833b2 --- /dev/null +++ b/vendor/k8s.io/heapster/influxdb/start @@ -0,0 +1,11 @@ +#! /bin/bash + +docker kill influxdb +docker rm influxdb + +docker run -d \ + -p 8083:8083 \ + -p 8086:8086 \ + -p 8090:8090 \ + -p 8099:8099 \ + --name influxdb kubernetes/heapster_influxdb diff --git a/vendor/k8s.io/heapster/integration/.jenkins.sh b/vendor/k8s.io/heapster/integration/.jenkins.sh new file mode 100755 index 0000000000..1b6581386a --- /dev/null +++ b/vendor/k8s.io/heapster/integration/.jenkins.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e -x + +export GOPATH="$JENKINS_HOME/workspace/project" +export GOBIN="$GOPATH/bin" +export PATH="$GOBIN:$PATH" + +# Kubernetes version(s) to run the integration tests against. +kube_version="1.2.4" + +if ! git diff --name-only origin/master | grep -c -E "*.go|*.sh|.*yaml|Makefile" &> /dev/null; then + echo "This PR does not touch files that require integration testing. Skipping integration tests!" + exit 0 +fi + +make -e SUPPORTED_KUBE_VERSIONS=$kube_version test-unit test-integration diff --git a/vendor/k8s.io/heapster/integration/framework.go b/vendor/k8s.io/heapster/integration/framework.go new file mode 100644 index 0000000000..f3903a6a63 --- /dev/null +++ b/vendor/k8s.io/heapster/integration/framework.go @@ -0,0 +1,517 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/api" + kclient "k8s.io/kubernetes/pkg/client/unversioned" + kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" + "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/runtime" +) + +type kubeFramework interface { + // Kube client + Client() *kclient.Client + + // Parses and Returns a replication Controller object contained in 'filePath' + ParseRC(filePath string) (*api.ReplicationController, error) + + // Parses and Returns a service object contained in 'filePath' + ParseService(filePath string) (*api.Service, error) + + // Creates a kube service. + CreateService(ns string, service *api.Service) (*api.Service, error) + + // Creates a namespace. + CreateNs(ns *api.Namespace) (*api.Namespace, error) + + // Creates a kube replication controller. + CreateRC(ns string, rc *api.ReplicationController) (*api.ReplicationController, error) + + // Deletes a namespace + DeleteNs(ns string) error + + // Destroy cluster + DestroyCluster() + + // Returns a url that provides access to a kubernetes service via the proxy on the apiserver. + // This url requires master auth. + GetProxyUrlForService(service *api.Service) string + + // Returns the node hostnames. + GetNodeNames() ([]string, error) + + // Returns the nodes. + GetNodes() (*api.NodeList, error) + + // Returns pod names in the cluster. + // TODO: Remove, or mix with namespace + GetRunningPodNames() ([]string, error) + + // Returns pods in the cluster running outside kubernetes-master. + GetPodsRunningOnNodes() ([]api.Pod, error) + + // Returns pods in the cluster. + GetAllRunningPods() ([]api.Pod, error) + + WaitUntilPodRunning(ns string, podLabels map[string]string, timeout time.Duration) error + WaitUntilServiceActive(svc *api.Service, timeout time.Duration) error +} + +type realKubeFramework struct { + // Kube client. + kubeClient *kclient.Client + + // The version of the kube cluster + version string + + // Master IP for this framework + masterIP string + + // The base directory of current kubernetes release. + baseDir string +} + +const imageUrlTemplate = "https://github.com/kubernetes/kubernetes/releases/download/v%s/kubernetes.tar.gz" + +var ( + kubeConfig = flag.String("kube_config", os.Getenv("HOME")+"/.kube/config", "Path to cluster info file.") + workDir = flag.String("work_dir", "/tmp/heapster_test", "Filesystem path where test files will be stored. Files will persist across runs to speed up tests.") +) + +func exists(path string) bool { + if _, err := os.Stat(path); err != nil { + glog.V(2).Infof("%q does not exist", path) + return false + } + return true +} + +const pathToGCEConfig = "cluster/gce/config-default.sh" + +func disableClusterMonitoring(kubeBaseDir string) error { + kubeConfigFilePath := filepath.Join(kubeBaseDir, pathToGCEConfig) + input, err := ioutil.ReadFile(kubeConfigFilePath) + if err != nil { + return err + } + + lines := strings.Split(string(input), "\n") + + for i, line := range lines { + if strings.Contains(line, "ENABLE_CLUSTER_MONITORING") { + lines[i] = "ENABLE_CLUSTER_MONITORING=false" + } else if strings.Contains(line, "NUM_MINIONS=") { + lines[i] = "NUM_MINIONS=2" + } + } + output := strings.Join(lines, "\n") + return ioutil.WriteFile(kubeConfigFilePath, []byte(output), 0644) +} + +func runKubeClusterCommand(kubeBaseDir, command string) ([]byte, error) { + cmd := exec.Command(filepath.Join(kubeBaseDir, "cluster", command)) + glog.V(2).Infof("about to run %v", cmd) + return cmd.CombinedOutput() +} + +func setupNewCluster(kubeBaseDir string) error { + cmd := "kube-up.sh" + destroyCluster(kubeBaseDir) + out, err := runKubeClusterCommand(kubeBaseDir, cmd) + if err != nil { + glog.Errorf("failed to bring up cluster - %q\n%s", err, out) + return fmt.Errorf("failed to bring up cluster - %q", err) + } + glog.V(2).Info(string(out)) + glog.V(2).Infof("Giving the cluster 30 sec to stabilize") + time.Sleep(30 * time.Second) + return nil +} + +func destroyCluster(kubeBaseDir string) error { + if kubeBaseDir == "" { + glog.Infof("Skipping cluster tear down since kubernetes repo base path is not set.") + return nil + } + glog.V(1).Info("Bringing down any existing kube cluster") + out, err := runKubeClusterCommand(kubeBaseDir, "kube-down.sh") + if err != nil { + glog.Errorf("failed to tear down cluster - %q\n%s", err, out) + return fmt.Errorf("failed to tear down kube cluster - %q", err) + } + + return nil +} + +func downloadRelease(workDir, version string) error { + // Temporary download path. + downloadPath := filepath.Join(workDir, "kube") + // Format url. + downloadUrl := fmt.Sprintf(imageUrlTemplate, version) + glog.V(1).Infof("About to download kube release using url: %q", downloadUrl) + + // Download kube code and store it in a temp dir. + if err := exec.Command("wget", downloadUrl, "-O", downloadPath).Run(); err != nil { + return fmt.Errorf("failed to wget kubernetes release @ %q - %v", downloadUrl, err) + } + + // Un-tar kube release. + if err := exec.Command("tar", "-xf", downloadPath, "-C", workDir).Run(); err != nil { + return fmt.Errorf("failed to un-tar kubernetes release at %q - %v", downloadPath, err) + } + return nil +} + +func getKubeClient() (string, *kclient.Client, error) { + c, err := kclientcmd.LoadFromFile(*kubeConfig) + if err != nil { + return "", nil, fmt.Errorf("error loading kubeConfig: %v", err.Error()) + } + if c.CurrentContext == "" || len(c.Clusters) == 0 { + return "", nil, fmt.Errorf("invalid kubeConfig: %+v", *c) + } + config, err := kclientcmd.NewDefaultClientConfig( + *c, + &kclientcmd.ConfigOverrides{ + ClusterInfo: kclientcmdapi.Cluster{ + APIVersion: "v1", + }, + }).ClientConfig() + if err != nil { + return "", nil, fmt.Errorf("error parsing kubeConfig: %v", err.Error()) + } + kubeClient, err := kclient.New(config) + if err != nil { + return "", nil, fmt.Errorf("error creating client - %q", err) + } + + return c.Clusters[c.CurrentContext].Server, kubeClient, nil +} + +func validateCluster(baseDir string) bool { + glog.V(1).Info("validating existing cluster") + out, err := runKubeClusterCommand(baseDir, "validate-cluster.sh") + if err != nil { + glog.V(1).Infof("cluster validation failed - %q\n %s", err, out) + return false + } + return true +} + +func requireNewCluster(baseDir, version string) bool { + // Setup kube client + _, kubeClient, err := getKubeClient() + if err != nil { + glog.V(1).Infof("kube client creation failed - %q", err) + return true + } + glog.V(1).Infof("checking if existing cluster can be used") + versionInfo, err := kubeClient.ServerVersion() + if err != nil { + glog.V(1).Infof("failed to get kube version info - %q", err) + return true + } + return !strings.Contains(versionInfo.GitVersion, version) +} + +func requireDownload(baseDir string) bool { + // Check that cluster scripts are present. + return !exists(filepath.Join(baseDir, "cluster", "kube-up.sh")) || + !exists(filepath.Join(baseDir, "cluster", "kube-down.sh")) || + !exists(filepath.Join(baseDir, "cluster", "validate-cluster.sh")) +} + +func downloadAndSetupCluster(version string) (baseDir string, err error) { + // Create a temp dir to store the kube release files. + tempDir := filepath.Join(*workDir, version) + if !exists(tempDir) { + if err := os.MkdirAll(tempDir, 0700); err != nil { + return "", fmt.Errorf("failed to create a temp dir at %s - %q", tempDir, err) + } + glog.V(1).Infof("Successfully setup work dir at %s", tempDir) + } + + kubeBaseDir := filepath.Join(tempDir, "kubernetes") + + if requireDownload(kubeBaseDir) { + if exists(kubeBaseDir) { + os.RemoveAll(kubeBaseDir) + } + if err := downloadRelease(tempDir, version); err != nil { + return "", err + } + glog.V(1).Infof("Successfully downloaded kubernetes release at %s", tempDir) + } + + // Disable monitoring + if err := disableClusterMonitoring(kubeBaseDir); err != nil { + return "", fmt.Errorf("failed to disable cluster monitoring in kube cluster config - %q", err) + } + glog.V(1).Info("Disabled cluster monitoring") + if !requireNewCluster(kubeBaseDir, version) { + glog.V(1).Infof("skipping cluster setup since a cluster with required version already exists") + return kubeBaseDir, nil + } + + // Setup kube cluster + glog.V(1).Infof("Setting up new kubernetes cluster version: %s", version) + if err := setupNewCluster(kubeBaseDir); err != nil { + // Cluster setup failed for some reason. + // Attempting to validate the cluster to see if it failed in the validate phase. + sleepDuration := 10 * time.Second + clusterReady := false + for i := 0; i < int(time.Minute/sleepDuration); i++ { + if !validateCluster(kubeBaseDir) { + glog.Infof("Retry validation after %v seconds.", sleepDuration/time.Second) + time.Sleep(sleepDuration) + } else { + clusterReady = true + break + } + } + if !clusterReady { + return "", fmt.Errorf("failed to setup cluster - %q", err) + } + } + glog.V(1).Infof("Successfully setup new kubernetes cluster version %s", version) + + return kubeBaseDir, nil +} + +func newKubeFramework(version string) (kubeFramework, error) { + var err error + kubeBaseDir := "" + if version != "" { + if len(strings.Split(version, ".")) != 3 { + return nil, fmt.Errorf("invalid kubernetes version specified - %q", version) + } + kubeBaseDir, err = downloadAndSetupCluster(version) + if err != nil { + return nil, err + } + } + + // Setup kube client + masterIP, kubeClient, err := getKubeClient() + if err != nil { + return nil, err + } + return &realKubeFramework{ + kubeClient: kubeClient, + baseDir: kubeBaseDir, + version: version, + masterIP: masterIP, + }, nil +} + +func (self *realKubeFramework) Client() *kclient.Client { + return self.kubeClient +} + +func (self *realKubeFramework) loadObject(filePath string) (runtime.Object, error) { + data, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("failed to read object: %v", err) + } + obj, _, err := api.Codecs.UniversalDecoder().Decode(data, nil, nil) + return obj, err +} + +func (self *realKubeFramework) ParseRC(filePath string) (*api.ReplicationController, error) { + obj, err := self.loadObject(filePath) + if err != nil { + return nil, err + } + + rc, ok := obj.(*api.ReplicationController) + if !ok { + return nil, fmt.Errorf("Failed to cast replicationController: %v", obj) + } + return rc, nil +} + +func (self *realKubeFramework) ParseService(filePath string) (*api.Service, error) { + obj, err := self.loadObject(filePath) + if err != nil { + return nil, err + } + service, ok := obj.(*api.Service) + if !ok { + return nil, fmt.Errorf("Failed to cast service: %v", obj) + } + return service, nil +} + +func (self *realKubeFramework) CreateService(ns string, service *api.Service) (*api.Service, error) { + service.Namespace = ns + newSvc, err := self.kubeClient.Services(ns).Create(service) + return newSvc, err +} + +func (self *realKubeFramework) DeleteNs(ns string) error { + + _, err := self.kubeClient.Namespaces().Get(ns) + if err != nil { + glog.V(0).Infof("Cannot get namespace %q. Skipping deletion: %s", ns, err) + return nil + } + glog.V(0).Infof("Deleting namespace %s", ns) + self.kubeClient.Namespaces().Delete(ns) + + for i := 0; i < 5; i++ { + glog.V(0).Infof("Checking for namespace %s", ns) + _, err := self.kubeClient.Namespaces().Get(ns) + if err != nil { + glog.V(0).Infof("%s doesn't exist", ns) + return nil + } + time.Sleep(10 * time.Second) + } + return fmt.Errorf("Namespace %s still exists", ns) +} + +func (self *realKubeFramework) CreateNs(ns *api.Namespace) (*api.Namespace, error) { + return self.kubeClient.Namespaces().Create(ns) +} + +func (self *realKubeFramework) CreateRC(ns string, rc *api.ReplicationController) (*api.ReplicationController, error) { + rc.Namespace = ns + return self.kubeClient.ReplicationControllers(ns).Create(rc) +} + +func (self *realKubeFramework) DestroyCluster() { + destroyCluster(self.baseDir) +} + +func (self *realKubeFramework) GetProxyUrlForService(service *api.Service) string { + return fmt.Sprintf("%s/api/v1/proxy/namespaces/default/services/%s/", self.masterIP, service.Name) +} + +func (self *realKubeFramework) GetNodeNames() ([]string, error) { + var nodes []string + nodeList, err := self.GetNodes() + if err != nil { + return nodes, err + } + for _, node := range nodeList.Items { + nodes = append(nodes, node.Name) + } + return nodes, nil +} + +func (self *realKubeFramework) GetNodes() (*api.NodeList, error) { + return self.kubeClient.Nodes().List(api.ListOptions{ + LabelSelector: labels.Everything(), + FieldSelector: fields.Everything(), + }) +} + +func (self *realKubeFramework) GetAllRunningPods() ([]api.Pod, error) { + return getRunningPods(true, self.kubeClient) +} + +func (self *realKubeFramework) GetPodsRunningOnNodes() ([]api.Pod, error) { + return getRunningPods(false, self.kubeClient) +} + +func getRunningPods(includeMaster bool, kubeClient *kclient.Client) ([]api.Pod, error) { + glog.V(0).Infof("Getting running pods") + podList, err := kubeClient.Pods(api.NamespaceAll).List(api.ListOptions{ + LabelSelector: labels.Everything(), + FieldSelector: fields.Everything(), + }) + if err != nil { + return nil, err + } + pods := []api.Pod{} + for _, pod := range podList.Items { + if pod.Status.Phase == api.PodRunning { + if includeMaster || !isMasterNode(pod.Spec.NodeName) { + pods = append(pods, pod) + } + } + } + return pods, nil +} + +func isMasterNode(nodeName string) bool { + return strings.Contains(nodeName, "kubernetes-master") +} + +func (self *realKubeFramework) GetRunningPodNames() ([]string, error) { + var pods []string + podList, err := self.GetAllRunningPods() + if err != nil { + return pods, err + } + for _, pod := range podList { + pods = append(pods, string(pod.Name)) + } + return pods, nil +} + +func (rkf *realKubeFramework) WaitUntilPodRunning(ns string, podLabels map[string]string, timeout time.Duration) error { + glog.V(2).Infof("Waiting for pod %v in %s...", podLabels, ns) + podsInterface := rkf.Client().Pods(ns) + for i := 0; i < int(timeout/time.Second); i++ { + podList, err := podsInterface.List(api.ListOptions{ + LabelSelector: labels.Set(podLabels).AsSelector(), + FieldSelector: fields.Everything(), + }) + if err != nil { + glog.V(1).Info(err) + return err + } + if len(podList.Items) > 0 { + podSpec := podList.Items[0] + if podSpec.Status.Phase == api.PodRunning { + return nil + } + } + time.Sleep(time.Second) + } + return fmt.Errorf("pod not in running state after %d", timeout/time.Second) +} + +func (rkf *realKubeFramework) WaitUntilServiceActive(svc *api.Service, timeout time.Duration) error { + glog.V(2).Infof("Waiting for endpoints in service %s/%s", svc.Namespace, svc.Name) + for i := 0; i < int(timeout/time.Second); i++ { + e, err := rkf.Client().Endpoints(svc.Namespace).Get(svc.Name) + if err != nil { + return err + } + if len(e.Subsets) > 0 { + return nil + } + time.Sleep(time.Second) + } + + return fmt.Errorf("Service %q not active after %d seconds - no endpoints found", svc.Name, timeout/time.Second) + +} diff --git a/vendor/k8s.io/heapster/integration/heapster_api_test.go b/vendor/k8s.io/heapster/integration/heapster_api_test.go new file mode 100644 index 0000000000..3de35808fe --- /dev/null +++ b/vendor/k8s.io/heapster/integration/heapster_api_test.go @@ -0,0 +1,1147 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "strings" + "testing" + "time" + + "github.com/golang/glog" + "github.com/stretchr/testify/require" + api_v1 "k8s.io/heapster/metrics/api/v1/types" + metrics_api "k8s.io/heapster/metrics/apis/metrics/v1alpha1" + "k8s.io/heapster/metrics/core" + kube_api "k8s.io/kubernetes/pkg/api" + apiErrors "k8s.io/kubernetes/pkg/api/errors" + kube_api_unv "k8s.io/kubernetes/pkg/api/unversioned" + kube_v1 "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/util/sets" +) + +const ( + targetTags = "kubernetes-minion" + heapsterBuildDir = "../deploy/docker" +) + +var ( + testZone = flag.String("test_zone", "us-central1-b", "GCE zone where the test will be executed") + kubeVersions = flag.String("kube_versions", "", "Comma separated list of kube versions to test against. By default will run the test against an existing cluster") + heapsterControllerFile = flag.String("heapster_controller", "../deploy/kube-config/standalone-test/heapster-controller.yaml", "Path to heapster replication controller file.") + heapsterServiceFile = flag.String("heapster_service", "../deploy/kube-config/standalone-test/heapster-service.yaml", "Path to heapster service file.") + heapsterImage = flag.String("heapster_image", "heapster:e2e_test", "heapster docker image that needs to be tested.") + avoidBuild = flag.Bool("nobuild", false, "When true, a new heapster docker image will not be created and pushed to test cluster nodes.") + namespace = flag.String("namespace", "heapster-e2e-tests", "namespace to be used for testing, it will be deleted at the beginning of the test if exists") + maxRetries = flag.Int("retries", 20, "Number of attempts before failing this test.") + runForever = flag.Bool("run_forever", false, "If true, the tests are run in a loop forever.") +) + +func deleteAll(fm kubeFramework, ns string, service *kube_api.Service, rc *kube_api.ReplicationController) error { + glog.V(2).Infof("Deleting ns %s...", ns) + err := fm.DeleteNs(ns) + if err != nil { + glog.V(2).Infof("Failed to delete %s", ns) + return err + } + glog.V(2).Infof("Deleted ns %s.", ns) + return nil +} + +func createAll(fm kubeFramework, ns string, service **kube_api.Service, rc **kube_api.ReplicationController) error { + glog.V(2).Infof("Creating ns %s...", ns) + namespace := kube_api.Namespace{ + TypeMeta: kube_api_unv.TypeMeta{ + Kind: "Namespace", + APIVersion: "v1", + }, + ObjectMeta: kube_api.ObjectMeta{ + Name: ns, + }, + } + if _, err := fm.CreateNs(&namespace); err != nil { + glog.V(2).Infof("Failed to create ns: %v", err) + return err + } + + glog.V(2).Infof("Created ns %s.", ns) + + glog.V(2).Infof("Creating rc %s/%s...", ns, (*rc).Name) + if newRc, err := fm.CreateRC(ns, *rc); err != nil { + glog.V(2).Infof("Failed to create rc: %v", err) + return err + } else { + *rc = newRc + } + glog.V(2).Infof("Created rc %s/%s.", ns, (*rc).Name) + + glog.V(2).Infof("Creating service %s/%s...", ns, (*service).Name) + if newSvc, err := fm.CreateService(ns, *service); err != nil { + glog.V(2).Infof("Failed to create service: %v", err) + return err + } else { + *service = newSvc + } + glog.V(2).Infof("Created service %s/%s.", ns, (*service).Name) + + return nil +} + +func removeHeapsterImage(fm kubeFramework, zone string) error { + glog.V(2).Infof("Removing heapster image.") + if err := removeDockerImage(*heapsterImage); err != nil { + glog.Errorf("Failed to remove Heapster image: %v", err) + } else { + glog.V(2).Infof("Heapster image removed.") + } + if nodes, err := fm.GetNodeNames(); err == nil { + for _, node := range nodes { + host := strings.Split(node, ".")[0] + cleanupRemoteHost(host, zone) + } + } else { + glog.Errorf("failed to cleanup nodes - %v", err) + } + return nil +} + +func buildAndPushHeapsterImage(hostnames []string, zone string) error { + glog.V(2).Info("Building and pushing Heapster image...") + curwd, err := os.Getwd() + if err != nil { + return err + } + if err := os.Chdir(heapsterBuildDir); err != nil { + return err + } + if err := buildDockerImage(*heapsterImage); err != nil { + return err + } + for _, host := range hostnames { + if err := copyDockerImage(*heapsterImage, host, zone); err != nil { + return err + } + } + glog.V(2).Info("Heapster image pushed.") + return os.Chdir(curwd) +} + +func getHeapsterRcAndSvc(fm kubeFramework) (*kube_api.Service, *kube_api.ReplicationController, error) { + // Add test docker image + rc, err := fm.ParseRC(*heapsterControllerFile) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse heapster controller - %v", err) + } + for i := range rc.Spec.Template.Spec.Containers { + rc.Spec.Template.Spec.Containers[i].Image = *heapsterImage + rc.Spec.Template.Spec.Containers[i].ImagePullPolicy = kube_api.PullNever + // increase logging level + rc.Spec.Template.Spec.Containers[i].Env = append(rc.Spec.Template.Spec.Containers[0].Env, kube_api.EnvVar{Name: "FLAGS", Value: "--vmodule=*=3"}) + } + + svc, err := fm.ParseService(*heapsterServiceFile) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse heapster service - %v", err) + } + + return svc, rc, nil +} + +func buildAndPushDockerImages(fm kubeFramework, zone string) error { + if *avoidBuild { + return nil + } + nodes, err := fm.GetNodeNames() + if err != nil { + return err + } + hostnames := []string{} + for _, node := range nodes { + hostnames = append(hostnames, strings.Split(node, ".")[0]) + } + + return buildAndPushHeapsterImage(hostnames, zone) +} + +const ( + metricsEndpoint = "/api/v1/metric-export" + metricsSchemaEndpoint = "/api/v1/metric-export-schema" +) + +func getTimeseries(fm kubeFramework, svc *kube_api.Service) ([]*api_v1.Timeseries, error) { + body, err := fm.Client().Get(). + Namespace(svc.Namespace). + Prefix("proxy"). + Resource("services"). + Name(svc.Name). + Suffix(metricsEndpoint). + Do().Raw() + if err != nil { + return nil, err + } + var timeseries []*api_v1.Timeseries + if err := json.Unmarshal(body, ×eries); err != nil { + glog.V(2).Infof("Timeseries error: %v %v", err, string(body)) + return nil, err + } + return timeseries, nil +} + +func getSchema(fm kubeFramework, svc *kube_api.Service) (*api_v1.TimeseriesSchema, error) { + body, err := fm.Client().Get(). + Namespace(svc.Namespace). + Prefix("proxy"). + Resource("services"). + Name(svc.Name). + Suffix(metricsSchemaEndpoint). + Do().Raw() + if err != nil { + return nil, err + } + var timeseriesSchema api_v1.TimeseriesSchema + if err := json.Unmarshal(body, ×eriesSchema); err != nil { + glog.V(2).Infof("Metrics schema error: %v %v", err, string(body)) + return nil, err + } + return ×eriesSchema, nil +} + +var expectedSystemContainers = map[string]struct{}{ + "machine": {}, + "kubelet": {}, + "kube-proxy": {}, + "system": {}, + "docker-daemon": {}, +} + +func isContainerBaseImageExpected(ts *api_v1.Timeseries) bool { + _, exists := expectedSystemContainers[ts.Labels[core.LabelContainerName.Key]] + return !exists +} + +func runMetricExportTest(fm kubeFramework, svc *kube_api.Service) error { + podList, err := fm.GetPodsRunningOnNodes() + if err != nil { + return err + } + expectedPods := make([]string, 0, len(podList)) + for _, pod := range podList { + expectedPods = append(expectedPods, pod.Name) + } + glog.V(0).Infof("Expected pods: %v", expectedPods) + + expectedNodes, err := fm.GetNodeNames() + if err != nil { + return err + } + glog.V(0).Infof("Expected nodes: %v", expectedNodes) + + timeseries, err := getTimeseries(fm, svc) + if err != nil { + return err + } + if len(timeseries) == 0 { + return fmt.Errorf("expected non zero timeseries") + } + schema, err := getSchema(fm, svc) + if err != nil { + return err + } + // Build a map of metric names to metric descriptors. + mdMap := map[string]*api_v1.MetricDescriptor{} + for idx := range schema.Metrics { + mdMap[schema.Metrics[idx].Name] = &schema.Metrics[idx] + } + actualPods := map[string]bool{} + actualNodes := map[string]bool{} + actualSystemContainers := map[string]map[string]struct{}{} + for _, ts := range timeseries { + // Verify the relevant labels are present. + // All common labels must be present. + podName, podMetric := ts.Labels[core.LabelPodName.Key] + + for _, label := range core.CommonLabels() { + _, exists := ts.Labels[label.Key] + if !exists { + return fmt.Errorf("timeseries: %v does not contain common label: %v", ts, label) + } + } + if podMetric { + for _, label := range core.PodLabels() { + _, exists := ts.Labels[label.Key] + if !exists { + return fmt.Errorf("timeseries: %v does not contain pod label: %v", ts, label) + } + } + } + + if podMetric { + actualPods[podName] = true + // Extra explicit check that the expecte metrics are there: + requiredLabels := []string{ + core.LabelPodNamespaceUID.Key, + core.LabelPodId.Key, + core.LabelHostID.Key, + // container name is checked later + } + for _, label := range requiredLabels { + _, exists := ts.Labels[label] + if !exists { + return fmt.Errorf("timeseries: %v does not contain required label: %v", ts, label) + } + } + + } else { + if cName, ok := ts.Labels[core.LabelContainerName.Key]; ok { + hostname, ok := ts.Labels[core.LabelHostname.Key] + if !ok { + return fmt.Errorf("hostname label missing on container %+v", ts) + } + + if cName == "machine" { + actualNodes[hostname] = true + } else { + for _, label := range core.ContainerLabels() { + if label == core.LabelContainerBaseImage && !isContainerBaseImageExpected(ts) { + continue + } + _, exists := ts.Labels[label.Key] + if !exists { + return fmt.Errorf("timeseries: %v does not contain container label: %v", ts, label) + } + } + } + + if _, exists := expectedSystemContainers[cName]; exists { + if actualSystemContainers[cName] == nil { + actualSystemContainers[cName] = map[string]struct{}{} + } + actualSystemContainers[cName][hostname] = struct{}{} + } + } else { + return fmt.Errorf("container_name label missing on timeseries - %v", ts) + } + } + + // Explicitly check for resource id + explicitRequirement := map[string][]string{ + core.MetricFilesystemUsage.MetricDescriptor.Name: {core.LabelResourceID.Key}, + core.MetricFilesystemLimit.MetricDescriptor.Name: {core.LabelResourceID.Key}, + core.MetricFilesystemAvailable.Name: {core.LabelResourceID.Key}} + + for metricName, points := range ts.Metrics { + md, exists := mdMap[metricName] + if !exists { + return fmt.Errorf("unexpected metric %q", metricName) + } + + for _, point := range points { + for _, label := range md.Labels { + _, exists := point.Labels[label.Key] + if !exists { + return fmt.Errorf("metric %q point %v does not contain metric label: %v", metricName, point, label) + } + } + } + + required := explicitRequirement[metricName] + for _, label := range required { + for _, point := range points { + _, exists := point.Labels[label] + if !exists { + return fmt.Errorf("metric %q point %v does not contain metric label: %v", metricName, point, label) + } + } + } + } + } + // Validate that system containers are running on all the nodes. + // This test could fail if one of the containers was down while the metrics sample was collected. + for cName, hosts := range actualSystemContainers { + for _, host := range expectedNodes { + if _, ok := hosts[host]; !ok { + return fmt.Errorf("System container %q not found on host: %q - %v", cName, host, actualSystemContainers) + } + } + } + + if err := expectedItemsExist(expectedPods, actualPods); err != nil { + return fmt.Errorf("expected pods don't exist %v.\nExpected: %v\nActual:%v", err, expectedPods, actualPods) + } + if err := expectedItemsExist(expectedNodes, actualNodes); err != nil { + return fmt.Errorf("expected nodes don't exist %v.\nExpected: %v\nActual:%v", err, expectedNodes, actualNodes) + } + + return nil +} + +func expectedItemsExist(expectedItems []string, actualItems map[string]bool) error { + for _, item := range expectedItems { + if _, found := actualItems[item]; !found { + return fmt.Errorf("missing %s", item) + } + } + return nil +} + +func getErrorCauses(err error) string { + serr, ok := err.(*apiErrors.StatusError) + if !ok { + return "" + } + var causes []string + for _, c := range serr.ErrStatus.Details.Causes { + causes = append(causes, c.Message) + } + return strings.Join(causes, ", ") +} + +var labelSelectorEverything = labels.Everything() + +func getDataFromProxy(fm kubeFramework, svc *kube_api.Service, url string) ([]byte, error) { + glog.V(2).Infof("Querying heapster: %s", url) + return fm.Client().Get(). + Namespace(svc.Namespace). + Prefix("proxy"). + Resource("services"). + Name(svc.Name). + Suffix(url). + Do().Raw() +} + +func getDataFromProxyWithSelector(fm kubeFramework, svc *kube_api.Service, url string, labelSelector *labels.Selector) ([]byte, error) { + glog.V(2).Infof("Querying heapster: %s", url) + return fm.Client().Get(). + Namespace(svc.Namespace). + Prefix("proxy"). + Resource("services"). + Name(svc.Name). + Suffix(url).LabelsSelectorParam(*labelSelector). + Do().Raw() +} + +func getMetricResultList(fm kubeFramework, svc *kube_api.Service, url string) (*api_v1.MetricResultList, error) { + body, err := getDataFromProxy(fm, svc, url) + if err != nil { + return nil, err + } + var data api_v1.MetricResultList + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return nil, err + } + if err := checkMetricResultListSanity(&data); err != nil { + return nil, err + } + return &data, nil +} + +func getMetricResult(fm kubeFramework, svc *kube_api.Service, url string) (*api_v1.MetricResult, error) { + body, err := getDataFromProxy(fm, svc, url) + if err != nil { + return nil, err + } + var data api_v1.MetricResult + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return nil, err + } + if err := checkMetricResultSanity(&data); err != nil { + return nil, err + } + return &data, nil +} + +func getStringResult(fm kubeFramework, svc *kube_api.Service, url string) ([]string, error) { + body, err := getDataFromProxy(fm, svc, url) + if err != nil { + return nil, err + } + var data []string + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return nil, err + } + if len(data) == 0 { + return nil, fmt.Errorf("empty string array") + } + return data, nil +} + +func checkMetricResultSanity(metrics *api_v1.MetricResult) error { + bytes, err := json.Marshal(*metrics) + if err != nil { + return err + } + stringVersion := string(bytes) + + if len(metrics.Metrics) == 0 { + return fmt.Errorf("empty metrics: %s", stringVersion) + } + // There should be recent metrics in the response. + if time.Now().Sub(metrics.LatestTimestamp).Seconds() > 120 { + return fmt.Errorf("corrupted last timestamp: %s", stringVersion) + } + // Metrics don't have to be sorted, so the oldest one can be first. + if time.Now().Sub(metrics.Metrics[0].Timestamp).Hours() > 1 { + return fmt.Errorf("corrupted timestamp: %s", stringVersion) + } + if metrics.Metrics[0].Value > 10000 { + return fmt.Errorf("value too big: %s", stringVersion) + } + return nil +} + +func checkMetricResultListSanity(metrics *api_v1.MetricResultList) error { + if len(metrics.Items) == 0 { + return fmt.Errorf("empty metrics") + } + for _, item := range metrics.Items { + err := checkMetricResultSanity(&item) + if err != nil { + return err + } + } + return nil +} + +func runModelTest(fm kubeFramework, svc *kube_api.Service) error { + podList, err := fm.GetPodsRunningOnNodes() + if err != nil { + return err + } + if len(podList) == 0 { + return fmt.Errorf("empty pod list") + } + nodeList, err := fm.GetNodeNames() + if err != nil { + return err + } + if len(nodeList) == 0 { + return fmt.Errorf("empty node list") + } + podNamesList := make([]string, 0, len(podList)) + for _, pod := range podList { + podNamesList = append(podNamesList, fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)) + } + + glog.V(0).Infof("Expected pods: %v", podNamesList) + glog.V(0).Infof("Expected nodes: %v", nodeList) + allkeys, err := getStringResult(fm, svc, "/api/v1/model/debug/allkeys") + if err != nil { + return fmt.Errorf("Failed to get debug information about keys: %v", err) + } + glog.V(0).Infof("Available Heapster metric sets: %v", allkeys) + + metricUrlsToCheck := []string{} + batchMetricsUrlsToCheck := []string{} + stringUrlsToCheck := []string{} + + /* TODO: enable once cluster aggregator is added. + metricUrlsToCheck = append(metricUrlsToCheck, + fmt.Sprintf("/api/v1/model/metrics/%s", "cpu-usage"), + ) + */ + + /* TODO: add once Cluster metrics aggregator is added. + "/api/v1/model/metrics", + "/api/v1/model/" + */ + stringUrlsToCheck = append(stringUrlsToCheck) + + for _, node := range nodeList { + metricUrlsToCheck = append(metricUrlsToCheck, + fmt.Sprintf("/api/v1/model/nodes/%s/metrics/%s", node, "cpu/usage_rate"), + fmt.Sprintf("/api/v1/model/nodes/%s/metrics/%s", node, "cpu-usage"), + ) + + stringUrlsToCheck = append(stringUrlsToCheck, + fmt.Sprintf("/api/v1/model/nodes/%s/metrics", node), + ) + } + + for _, pod := range podList { + containerName := pod.Spec.Containers[0].Name + + metricUrlsToCheck = append(metricUrlsToCheck, + fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics/%s", pod.Namespace, pod.Name, "cpu/usage_rate"), + fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics/%s", pod.Namespace, pod.Name, "cpu-usage"), + fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics/%s", pod.Namespace, pod.Name, containerName, "cpu/usage_rate"), + fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics/%s", pod.Namespace, pod.Name, containerName, "cpu-usage"), + fmt.Sprintf("/api/v1/model/namespaces/%s/metrics/%s", pod.Namespace, "cpu/usage_rate"), + fmt.Sprintf("/api/v1/model/namespaces/%s/metrics/%s", pod.Namespace, "cpu-usage"), + ) + + batchMetricsUrlsToCheck = append(batchMetricsUrlsToCheck, + fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s,%s/metrics/%s", pod.Namespace, pod.Name, pod.Name, "cpu/usage_rate"), + fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s,%s/metrics/%s", pod.Namespace, pod.Name, pod.Name, "cpu-usage"), + ) + + stringUrlsToCheck = append(stringUrlsToCheck, + fmt.Sprintf("/api/v1/model/namespaces/%s/metrics", pod.Namespace), + fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/metrics", pod.Namespace, pod.Name), + fmt.Sprintf("/api/v1/model/namespaces/%s/pods/%s/containers/%s/metrics", pod.Namespace, pod.Name, containerName), + ) + } + + for _, url := range metricUrlsToCheck { + _, err := getMetricResult(fm, svc, url) + if err != nil { + return fmt.Errorf("error while querying %s: %v", url, err) + } + } + + for _, url := range batchMetricsUrlsToCheck { + _, err := getMetricResultList(fm, svc, url) + if err != nil { + return fmt.Errorf("error while querying %s: %v", url, err) + } + } + + for _, url := range stringUrlsToCheck { + _, err := getStringResult(fm, svc, url) + if err != nil { + return fmt.Errorf("error while querying %s: %v", url, err) + } + } + return nil +} + +const ( + apiPrefix = "apis" + metricsApiGroupName = "metrics" + metricsApiVersion = "v1alpha1" +) + +var baseMetricsUrl = fmt.Sprintf("%s/%s/%s", apiPrefix, metricsApiGroupName, metricsApiVersion) + +func checkUsage(res kube_v1.ResourceList) error { + if _, found := res[kube_v1.ResourceCPU]; !found { + return fmt.Errorf("Cpu not found") + } + if _, found := res[kube_v1.ResourceMemory]; !found { + return fmt.Errorf("Memory not found") + } + return nil +} + +func getPodMetrics(fm kubeFramework, svc *kube_api.Service, pod kube_api.Pod) (*metrics_api.PodMetrics, error) { + url := fmt.Sprintf("%s/namespaces/%s/pods/%s", baseMetricsUrl, pod.Namespace, pod.Name) + body, err := getDataFromProxy(fm, svc, url) + if err != nil { + return nil, err + } + var data metrics_api.PodMetrics + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return nil, err + } + return &data, nil +} + +func getAllPodsInNamespaceMetrics(fm kubeFramework, svc *kube_api.Service, namespace string) (metrics_api.PodMetricsList, error) { + url := fmt.Sprintf("%s/namespaces/%s/pods/", baseMetricsUrl, namespace) + return getPodMetricsList(fm, svc, url, &labelSelectorEverything) +} + +func getAllPodsMetrics(fm kubeFramework, svc *kube_api.Service) (metrics_api.PodMetricsList, error) { + url := fmt.Sprintf("%s/pods/", baseMetricsUrl) + selector := labels.Everything() + return getPodMetricsList(fm, svc, url, &selector) +} + +func getLabelSelectedPodMetrics(fm kubeFramework, svc *kube_api.Service, namespace string, labelSelector *labels.Selector) (metrics_api.PodMetricsList, error) { + url := fmt.Sprintf("%s/namespaces/%s/pods/", baseMetricsUrl, namespace) + return getPodMetricsList(fm, svc, url, labelSelector) +} + +func getPodMetricsList(fm kubeFramework, svc *kube_api.Service, url string, labelSelector *labels.Selector) (metrics_api.PodMetricsList, error) { + body, err := getDataFromProxyWithSelector(fm, svc, url, labelSelector) + if err != nil { + return metrics_api.PodMetricsList{}, err + } + var data metrics_api.PodMetricsList + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return metrics_api.PodMetricsList{}, err + } + return data, nil +} + +func checkSinglePodMetrics(metrics *metrics_api.PodMetrics, pod *kube_api.Pod) error { + if metrics.Name != pod.Name { + return fmt.Errorf("Wrong pod name: expected %v, got %v", pod.Name, metrics.Name) + } + if metrics.Namespace != pod.Namespace { + return fmt.Errorf("Wrong pod namespace: expected %v, got %v", pod.Namespace, metrics.Namespace) + } + if len(pod.Spec.Containers) != len(metrics.Containers) { + return fmt.Errorf("Wrong number of containers in returned metrics: expected %v, got %v", len(pod.Spec.Containers), len(metrics.Containers)) + } + for _, c := range metrics.Containers { + if err := checkUsage(c.Usage); err != nil { + return err + } + } + return nil +} + +func getSingleNodeMetrics(fm kubeFramework, svc *kube_api.Service, node string) (*metrics_api.NodeMetrics, error) { + url := fmt.Sprintf("%s/nodes/%s", baseMetricsUrl, node) + body, err := getDataFromProxy(fm, svc, url) + if err != nil { + return nil, err + } + var data metrics_api.NodeMetrics + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return nil, err + } + return &data, nil +} + +func getNodeMetricsList(fm kubeFramework, svc *kube_api.Service, url string, labelSelector *labels.Selector) (metrics_api.NodeMetricsList, error) { + body, err := getDataFromProxyWithSelector(fm, svc, url, labelSelector) + if err != nil { + return metrics_api.NodeMetricsList{}, err + } + var data metrics_api.NodeMetricsList + if err := json.Unmarshal(body, &data); err != nil { + glog.V(2).Infof("response body: %v", string(body)) + return metrics_api.NodeMetricsList{}, err + } + return data, nil +} + +func getLabelSelectedNodeMetrics(fm kubeFramework, svc *kube_api.Service, labelSelector *labels.Selector) (metrics_api.NodeMetricsList, error) { + url := fmt.Sprintf("%s/nodes", baseMetricsUrl) + return getNodeMetricsList(fm, svc, url, labelSelector) +} + +func getAllNodeMetrics(fm kubeFramework, svc *kube_api.Service) (metrics_api.NodeMetricsList, error) { + url := fmt.Sprintf("%s/nodes", baseMetricsUrl) + selector := labels.Everything() + return getNodeMetricsList(fm, svc, url, &selector) +} + +func runSingleNodeMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + nodeList, err := fm.GetNodeNames() + if err != nil { + return err + } + if len(nodeList) == 0 { + return fmt.Errorf("empty node list") + } + + for _, node := range nodeList { + metrics, err := getSingleNodeMetrics(fm, svc, node) + if err != nil { + return err + } + if metrics.Name != node { + return fmt.Errorf("Wrong node name: expected %v, got %v", node, metrics.Name) + } + if err := checkUsage(metrics.Usage); err != nil { + return err + } + } + return nil +} + +func runLabelSelectorNodeMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + nodeList, err := fm.GetNodes() + if err != nil { + return err + } + if len(nodeList.Items) == 0 { + return fmt.Errorf("empty node list") + } + labelMap := make(map[string]map[string]kube_api.Node) + for _, n := range nodeList.Items { + for label, value := range n.Labels { + selector := label + "=" + value + if _, found := labelMap[selector]; !found { + labelMap[selector] = make(map[string]kube_api.Node) + } + labelMap[selector][n.Name] = n + } + } + + for selector, nodesWithLabel := range labelMap { + sel, err := labels.Parse(selector) + if err != nil { + return err + } + metrics, err := getLabelSelectedNodeMetrics(fm, svc, &sel) + if err != nil { + return err + } + if len(metrics.Items) != len(nodesWithLabel) { + return fmt.Errorf("Wrong number of label selected node metrics: expected %v, got %v", len(nodesWithLabel), len(metrics.Items)) + } + for _, nodeMetric := range metrics.Items { + node := nodesWithLabel[nodeMetric.Name] + if nodeMetric.Name != node.Name { + return fmt.Errorf("Wrong node name: expected %v, got %v", node.Name, nodeMetric.Name) + } + if err := checkUsage(nodeMetric.Usage); err != nil { + return err + } + } + } + return nil +} + +func runAllNodesMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + nodeList, err := fm.GetNodeNames() + if err != nil { + return err + } + if len(nodeList) == 0 { + return fmt.Errorf("empty node list") + } + + nodeNames := sets.NewString(nodeList...) + metrics, err := getAllNodeMetrics(fm, svc) + if err != nil { + return err + } + + if len(metrics.Items) != len(nodeList) { + return fmt.Errorf("Wrong number of all node metrics: expected %v, got %v", len(nodeList), len(metrics.Items)) + } + for _, nodeMetrics := range metrics.Items { + if !nodeNames.Has(nodeMetrics.Name) { + return fmt.Errorf("Unexpected node name: %v, expected one of: %v", nodeMetrics.Name, nodeList) + } + if err := checkUsage(nodeMetrics.Usage); err != nil { + return err + } + } + return nil +} + +func runSinglePodMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + podList, err := fm.GetAllRunningPods() + if err != nil { + return err + } + if len(podList) == 0 { + return fmt.Errorf("empty pod list") + } + for _, pod := range podList { + metrics, err := getPodMetrics(fm, svc, pod) + if err != nil { + return err + } + err = checkSinglePodMetrics(metrics, &pod) + if err != nil { + return err + } + } + return nil +} + +func runAllPodsInNamespaceMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + podList, err := fm.GetAllRunningPods() + if err != nil { + return err + } + if len(podList) == 0 { + return fmt.Errorf("empty pod list") + } + nsToPods := make(map[string]map[string]kube_api.Pod) + for _, pod := range podList { + if _, found := nsToPods[pod.Namespace]; !found { + nsToPods[pod.Namespace] = make(map[string]kube_api.Pod) + } + nsToPods[pod.Namespace][pod.Name] = pod + } + + for ns, podMap := range nsToPods { + metrics, err := getAllPodsInNamespaceMetrics(fm, svc, ns) + if err != nil { + return err + } + + if len(metrics.Items) != len(nsToPods[ns]) { + return fmt.Errorf("Wrong number of metrics of all pods in a namespace: expected %v, got %v", len(nsToPods[ns]), len(metrics.Items)) + } + for _, podMetric := range metrics.Items { + pod := podMap[podMetric.Name] + err := checkSinglePodMetrics(&podMetric, &pod) + if err != nil { + return err + } + } + } + return nil +} + +func runAllPodsMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + podList, err := fm.GetAllRunningPods() + if err != nil { + return err + } + if len(podList) == 0 { + return fmt.Errorf("empty pod list") + } + pods := make(map[string]kube_api.Pod) + for _, p := range podList { + pods[p.Namespace+"/"+p.Name] = p + } + + metrics, err := getAllPodsMetrics(fm, svc) + if err != nil { + return err + } + + if len(metrics.Items) != len(podList) { + return fmt.Errorf("Wrong number of all pod metrics: expected %v, got %v", len(podList), len(metrics.Items)) + } + for _, podMetric := range metrics.Items { + pod := pods[podMetric.Namespace+"/"+podMetric.Name] + err := checkSinglePodMetrics(&podMetric, &pod) + if err != nil { + return err + } + } + return nil +} + +func runLabelSelectorPodMetricsApiTest(fm kubeFramework, svc *kube_api.Service) error { + podList, err := fm.GetAllRunningPods() + if err != nil { + return err + } + if len(podList) == 0 { + return fmt.Errorf("empty pod list") + } + nsToPods := make(map[string][]kube_api.Pod) + for _, pod := range podList { + nsToPods[pod.Namespace] = append(nsToPods[pod.Namespace], pod) + } + + for ns, podsInNamespace := range nsToPods { + labelMap := make(map[string]map[string]kube_api.Pod) + for _, p := range podsInNamespace { + for label, name := range p.Labels { + selector := label + "=" + name + if _, found := labelMap[selector]; !found { + labelMap[selector] = make(map[string]kube_api.Pod) + } + labelMap[selector][p.Name] = p + } + } + for selector, podsWithLabel := range labelMap { + sel, err := labels.Parse(selector) + if err != nil { + return err + } + metrics, err := getLabelSelectedPodMetrics(fm, svc, ns, &sel) + if err != nil { + return err + } + if len(metrics.Items) != len(podsWithLabel) { + return fmt.Errorf("Wrong number of label selected pod metrics: expected %v, got %v", len(podsWithLabel), len(metrics.Items)) + } + for _, podMetric := range metrics.Items { + pod := podsWithLabel[podMetric.Name] + err := checkSinglePodMetrics(&podMetric, &pod) + if err != nil { + return err + } + } + } + } + return nil +} + +func apiTest(kubeVersion string, zone string) error { + fm, err := newKubeFramework(kubeVersion) + if err != nil { + return err + } + if err := buildAndPushDockerImages(fm, zone); err != nil { + return err + } + // Create heapster pod and service. + svc, rc, err := getHeapsterRcAndSvc(fm) + if err != nil { + return err + } + ns := *namespace + if err := deleteAll(fm, ns, svc, rc); err != nil { + return err + } + if err := createAll(fm, ns, &svc, &rc); err != nil { + return err + } + if err := fm.WaitUntilPodRunning(ns, rc.Spec.Template.Labels, time.Minute); err != nil { + return err + } + if err := fm.WaitUntilServiceActive(svc, time.Minute); err != nil { + return err + } + testFuncs := []func() error{ + func() error { + glog.V(2).Infof("Heapster metric export test...") + err := runMetricExportTest(fm, svc) + if err == nil { + glog.V(2).Infof("Heapster metric export test: OK") + } else { + glog.V(2).Infof("Heapster metric export test: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Model test") + err := runModelTest(fm, svc) + if err == nil { + glog.V(2).Infof("Model test: OK") + } else { + glog.V(2).Infof("Model test: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - single pod") + err := runSinglePodMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - single pod: OK") + } else { + glog.V(2).Infof("Metrics API test - single pod: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - All pods in a namespace") + err := runAllPodsInNamespaceMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - All pods in a namespace: OK") + } else { + glog.V(2).Infof("Metrics API test - All pods in a namespace: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - all pods") + err := runAllPodsMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - all pods: OK") + } else { + glog.V(2).Infof("Metrics API test - all pods: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - label selector for pods") + err := runLabelSelectorPodMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - label selector for pods: OK") + } else { + glog.V(2).Infof("Metrics API test - label selector for pods: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - single node") + err := runSingleNodeMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - single node: OK") + } else { + glog.V(2).Infof("Metrics API test - single node: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - label selector for nodes") + err := runLabelSelectorNodeMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - label selector for nodes: OK") + } else { + glog.V(2).Infof("Metrics API test - label selector for nodes: error: %v", err) + } + return err + }, + func() error { + glog.V(2).Infof("Metrics API test - all nodes") + err := runAllNodesMetricsApiTest(fm, svc) + if err == nil { + glog.V(2).Infof("Metrics API test - all nodes: OK") + } else { + glog.V(2).Infof("Metrics API test - all nodes: error: %v", err) + } + return err + }, + } + attempts := *maxRetries + glog.Infof("Starting tests") + for { + var err error + for _, testFunc := range testFuncs { + if err = testFunc(); err != nil { + break + } + } + if *runForever { + continue + } + if err == nil { + glog.V(2).Infof("All tests passed.") + break + } + if attempts == 0 { + glog.V(2).Info("Too many attempts.") + return err + } + glog.V(2).Infof("Some tests failed. Retrying.") + attempts-- + time.Sleep(time.Second * 10) + } + deleteAll(fm, ns, svc, rc) + removeHeapsterImage(fm, zone) + return nil +} + +func runApiTest() error { + tempDir, err := ioutil.TempDir("", "deploy") + if err != nil { + return nil + } + defer os.RemoveAll(tempDir) + if *kubeVersions == "" { + return apiTest("", *testZone) + } + kubeVersionsList := strings.Split(*kubeVersions, ",") + for _, kubeVersion := range kubeVersionsList { + if err := apiTest(kubeVersion, *testZone); err != nil { + return err + } + } + return nil +} + +func TestHeapster(t *testing.T) { + if testing.Short() { + t.Skip("skipping heapster kubernetes integration test.") + } + require.NoError(t, runApiTest()) +} diff --git a/vendor/k8s.io/heapster/integration/model_test.go b/vendor/k8s.io/heapster/integration/model_test.go new file mode 100644 index 0000000000..539c204d97 --- /dev/null +++ b/vendor/k8s.io/heapster/integration/model_test.go @@ -0,0 +1,158 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +/* +Commented out until the new data flow supports model api + +import ( + "fmt" + "testing" + "time" + + cadvisor "github.com/google/cadvisor/info/v1" + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/manager" + model_api "k8s.io/heapster/metrics/model" + "k8s.io/heapster/metrics/sinks" + sink_api "k8s.io/heapster/metrics/sinks/api" + "k8s.io/heapster/metrics/sinks/cache" + source_api "k8s.io/heapster/metrics/sources/api" +) + +type testSource struct { + createTimestamp time.Time +} + +const ( + podCount = 10 + testNamespace = "testnamespace" + loadAverageMilli = 300 +) + +func (t testSource) buildPods(start time.Time) []source_api.Pod { + + timeElapsed := start.Sub(t.createTimestamp) + + result := []source_api.Pod{} + for i := 0; i < podCount; i++ { + stat := source_api.ContainerStats{ + ContainerStats: cadvisor.ContainerStats{ + Timestamp: start.Add(time.Millisecond * 500), + Cpu: cadvisor.CpuStats{ + Usage: cadvisor.CpuUsage{ + Total: uint64(loadAverageMilli * 1000000 * timeElapsed.Seconds()), + User: uint64(loadAverageMilli * 1000000 * timeElapsed.Seconds()), + System: 0, + }, + LoadAverage: loadAverageMilli, + }, + }, + } + + pod := source_api.Pod{ + PodMetadata: source_api.PodMetadata{ + Name: fmt.Sprintf("pod-%d", i), + Namespace: testNamespace, + NamespaceUID: testNamespace + "UID", + ID: fmt.Sprintf("pid-%d", i), + Hostname: fmt.Sprintf("node-%d", i), + Status: "Running", + PodIP: fmt.Sprintf("10.0.0.%d", i), + }, + Containers: []source_api.Container{ + { + Hostname: fmt.Sprintf("node-%d", i), + ExternalID: fmt.Sprintf("cont-%d", i), + Name: "cont", + Spec: source_api.ContainerSpec{ + ContainerSpec: cadvisor.ContainerSpec{ + HasCpu: true, + Cpu: cadvisor.CpuSpec{ + Limit: 500, + MaxLimit: 600, + }, + }, + }, + Stats: []*source_api.ContainerStats{&stat}, + }, + }, + } + result = append(result, pod) + } + return result +} + +func (t testSource) GetInfo(start, end time.Time) (source_api.AggregateData, error) { + return source_api.AggregateData{ + Pods: t.buildPods(start), + }, nil +} + +func (t testSource) DebugInfo() string { + return "test-debug-info" +} + +func (t testSource) Name() string { + return "test-source" +} + +func newTestSource() source_api.Source { + return &testSource{ + createTimestamp: time.Now().Add(-10 * time.Second), + } +} + +func TestModelMetricPassing(t *testing.T) { + if testing.Short() { + t.Skip("skipping heapster model integration test.") + } + assert := assert.New(t) + resolution := 2 * time.Second + + sources := []source_api.Source{newTestSource()} + cache := cache.NewCache(time.Hour, time.Hour) + assert.NotNil(cache) + sinkManager, err := sinks.NewExternalSinkManager([]sink_api.ExternalSink{}, cache, resolution) + assert.NoError(err) + + manager, err := manager.NewManager(sources, sinkManager, resolution, time.Hour, cache, true, resolution, resolution) + assert.NoError(err) + start := time.Now() + + manager.Start() + defer manager.Stop() + time.Sleep(10 * time.Second) + + model := manager.GetModel() + pods := model.GetPods(testNamespace) + assert.Equal(podCount, len(pods)) + + metrics, _, err := model.GetPodMetric(model_api.PodMetricRequest{ + NamespaceName: testNamespace, + PodName: "pod-0", + MetricRequest: model_api.MetricRequest{ + Start: start, + End: time.Time{}, + MetricName: "cpu-usage", + }, + }) + assert.NoError(err) + //TODO: Expect more than 1 metric once #551 is fixed + assert.NotEmpty(metrics) + assert.InEpsilon(loadAverageMilli, metrics[0].Value, 50) +} +*/ diff --git a/vendor/k8s.io/heapster/integration/utils.go b/vendor/k8s.io/heapster/integration/utils.go new file mode 100644 index 0000000000..173f7ba121 --- /dev/null +++ b/vendor/k8s.io/heapster/integration/utils.go @@ -0,0 +1,74 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package integration + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" +) + +func buildDockerImage(imageName string) error { + out, err := exec.Command("./build.sh", imageName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to build docker binary (%q) - %q", err, out) + } + + return nil +} + +func copyDockerImage(imageName, hostname, zone string) error { + tempfile, err := ioutil.TempFile("", hostname) + if err != nil { + return err + } + defer os.Remove(tempfile.Name()) + out, err := exec.Command("docker", "save", "-o", tempfile.Name(), imageName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to save docker binary (%q) - %q", err, out) + } + remoteFile := path.Join("/tmp", path.Base(tempfile.Name())) + out, err = exec.Command("gcloud", "compute", "copy-files", "--zone", zone, tempfile.Name(), fmt.Sprintf("%s:%s", hostname, remoteFile)).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to push docker binary to %q (%q) - %q", hostname, err, out) + } + out, err = exec.Command("gcloud", "compute", "ssh", "--zone", zone, hostname, "--command", fmt.Sprintf("sudo docker load -i %s", remoteFile)).CombinedOutput() + if err != nil { + err = fmt.Errorf("failed to load docker image %q using temp file %q on host %q (%q) - %q", imageName, remoteFile, hostname, err, out) + } + out, rmErr := exec.Command("gcloud", "compute", "ssh", "--zone", zone, hostname, "--command", fmt.Sprintf("sudo rm -f %s", remoteFile)).CombinedOutput() + if rmErr != nil { + if err != nil { + rmErr = fmt.Errorf("%v\nfailed to remove tempfile on host %q (%q) - %q", err, hostname, err, out) + } + return rmErr + } + return err +} + +func removeDockerImage(imageName string) error { + out, err := exec.Command("docker", "rmi", "-f", imageName).CombinedOutput() + if err != nil { + return fmt.Errorf("failed to remove docker image %q (%q) - %q", imageName, err, out) + } + return nil +} + +func cleanupRemoteHost(hostname, zone string) { + _ = exec.Command("gcloud", "compute", "ssh", "--zone", zone, hostname, "--command", "\"sudo docker rm `docker ps -a -q`\"") + _ = exec.Command("gcloud", "compute", "ssh", "--zone", zone, hostname, "--command", "\"sudo docker rmi `docker images -a -q`\"") +} diff --git a/vendor/k8s.io/heapster/kafka/Dockerfile b/vendor/k8s.io/heapster/kafka/Dockerfile new file mode 100644 index 0000000000..7b9a7ca52b --- /dev/null +++ b/vendor/k8s.io/heapster/kafka/Dockerfile @@ -0,0 +1,27 @@ +# Kafka with Zookeeper for kubernetes' heapster + +FROM java:openjdk-8-jre + +ENV DEBIAN_FRONTEND noninteractive +ENV SCALA_VERSION 2.11 +ENV KAFKA_VERSION 0.8.2.1 +ENV KAFKA_HOME /opt/kafka_"$SCALA_VERSION"-"$KAFKA_VERSION" + +RUN apt-get update && \ + apt-get install -y zookeeper wget supervisor dnsutils && \ + rm -rf /var/lib/apt/lists/* && \ + apt-get clean && \ + wget -q http://apache.mirrors.spacedump.net/kafka/"$KAFKA_VERSION"/kafka_"$SCALA_VERSION"-"$KAFKA_VERSION".tgz -O /tmp/kafka_"$SCALA_VERSION"-"$KAFKA_VERSION".tgz && \ + tar xfz /tmp/kafka_"$SCALA_VERSION"-"$KAFKA_VERSION".tgz -C /opt && \ + rm /tmp/kafka_"$SCALA_VERSION"-"$KAFKA_VERSION".tgz + +ADD scripts/start-kafka.sh /usr/bin/start-kafka.sh +RUN chmod +x /usr/bin/start-kafka.sh + +# Supervisor config +ADD supervisor/kafka.conf supervisor/zookeeper.conf /etc/supervisor/conf.d/ + +# 2181 is zookeeper, 9092 is kafka +EXPOSE 2181 9092 + +CMD ["supervisord", "-n"] diff --git a/vendor/k8s.io/heapster/kafka/README.md b/vendor/k8s.io/heapster/kafka/README.md new file mode 100644 index 0000000000..ec905df3d8 --- /dev/null +++ b/vendor/k8s.io/heapster/kafka/README.md @@ -0,0 +1,11 @@ +heapster_kafka +================ + +Monitoring for Kubernetes container metrics + +this docker image runs both Zookeeper and Kafka in the same container. +This means: + +No dependency on an external Zookeeper host, or linking to another container +Zookeeper and Kafka are configured to work together out of the box + diff --git a/vendor/k8s.io/heapster/kafka/build.sh b/vendor/k8s.io/heapster/kafka/build.sh new file mode 100644 index 0000000000..0a84c7dd4c --- /dev/null +++ b/vendor/k8s.io/heapster/kafka/build.sh @@ -0,0 +1,5 @@ +#! /bin/bash + +pushd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +docker build -t heapster_kafka:canary . +popd diff --git a/vendor/k8s.io/heapster/kafka/scripts/start-kafka.sh b/vendor/k8s.io/heapster/kafka/scripts/start-kafka.sh new file mode 100644 index 0000000000..ee56b2f5b2 --- /dev/null +++ b/vendor/k8s.io/heapster/kafka/scripts/start-kafka.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +# Optional ENV variables: +# * ADVERTISED_HOST: the external ip for the container, e.g. `docker-machine ip \`docker-machine active\`` +# * ADVERTISED_PORT: the external port for Kafka, e.g. 9092 +# * ZK_CHROOT: the zookeeper chroot that's used by Kafka (without / prefix), e.g. "kafka" +# * LOG_RETENTION_HOURS: the minimum age of a log file in hours to be eligible for deletion (default is 168, for 1 week) +# * LOG_RETENTION_BYTES: configure the size at which segments are pruned from the log, (default is 1073741824, for 1GB) +# * NUM_PARTITIONS: configure the default number of log partitions per topic + +# Configure advertised host/port if we run in helios +if [ ! -z "$HELIOS_PORT_kafka" ]; then + ADVERTISED_HOST=`echo $HELIOS_PORT_kafka | cut -d':' -f 1 | xargs -n 1 dig +short | tail -n 1` + ADVERTISED_PORT=`echo $HELIOS_PORT_kafka | cut -d':' -f 2` +fi + +# Set the external host and port +if [ ! -z "$ADVERTISED_HOST" ]; then + echo "advertised host: $ADVERTISED_HOST" + sed -r -i "s/#(advertised.host.name)=(.*)/\1=$ADVERTISED_HOST/g" $KAFKA_HOME/config/server.properties +fi +if [ ! -z "$ADVERTISED_PORT" ]; then + echo "advertised port: $ADVERTISED_PORT" + sed -r -i "s/#(advertised.port)=(.*)/\1=$ADVERTISED_PORT/g" $KAFKA_HOME/config/server.properties +fi + +# Set the zookeeper chroot +if [ ! -z "$ZK_CHROOT" ]; then + # wait for zookeeper to start up + until /usr/share/zookeeper/bin/zkServer.sh status; do + sleep 0.1 + done + + # create the chroot node + echo "create /$ZK_CHROOT \"\"" | /usr/share/zookeeper/bin/zkCli.sh || { + echo "can't create chroot in zookeeper, exit" + exit 1 + } + + # configure kafka + sed -r -i "s/(zookeeper.connect)=(.*)/\1=localhost:2181\/$ZK_CHROOT/g" $KAFKA_HOME/config/server.properties +fi + +# Allow specification of log retention policies +if [ ! -z "$LOG_RETENTION_HOURS" ]; then + echo "log retention hours: $LOG_RETENTION_HOURS" + sed -r -i "s/(log.retention.hours)=(.*)/\1=$LOG_RETENTION_HOURS/g" $KAFKA_HOME/config/server.properties +fi +if [ ! -z "$LOG_RETENTION_BYTES" ]; then + echo "log retention bytes: $LOG_RETENTION_BYTES" + sed -r -i "s/#(log.retention.bytes)=(.*)/\1=$LOG_RETENTION_BYTES/g" $KAFKA_HOME/config/server.properties +fi + +# Configure the default number of log partitions per topic +if [ ! -z "$NUM_PARTITIONS" ]; then + echo "default number of partition: $NUM_PARTITIONS" + sed -r -i "s/(num.partitions)=(.*)/\1=$NUM_PARTITIONS/g" $KAFKA_HOME/config/server.properties +fi + +# Run Kafka +$KAFKA_HOME/bin/kafka-server-start.sh $KAFKA_HOME/config/server.properties + diff --git a/vendor/k8s.io/heapster/kafka/supervisor/kafka.conf b/vendor/k8s.io/heapster/kafka/supervisor/kafka.conf new file mode 100644 index 0000000000..cf4104264c --- /dev/null +++ b/vendor/k8s.io/heapster/kafka/supervisor/kafka.conf @@ -0,0 +1,4 @@ +[program:kafka] +command=/usr/bin/start-kafka.sh +autostart=true +autorestart=true diff --git a/vendor/k8s.io/heapster/kafka/supervisor/zookeeper.conf b/vendor/k8s.io/heapster/kafka/supervisor/zookeeper.conf new file mode 100644 index 0000000000..1c8de37f44 --- /dev/null +++ b/vendor/k8s.io/heapster/kafka/supervisor/zookeeper.conf @@ -0,0 +1,4 @@ +[program:zookeeper] +command=/usr/share/zookeeper/bin/zkServer.sh start-foreground +autostart=true +autorestart=true diff --git a/vendor/k8s.io/heapster/metrics/api/v1/api.go b/vendor/k8s.io/heapster/metrics/api/v1/api.go new file mode 100644 index 0000000000..ac0ddc40a6 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/api.go @@ -0,0 +1,281 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + "time" + + restful "github.com/emicklei/go-restful" + + "k8s.io/heapster/metrics/api/v1/types" + "k8s.io/heapster/metrics/core" + metricsink "k8s.io/heapster/metrics/sinks/metric" +) + +type Api struct { + runningInKubernetes bool + metricSink *metricsink.MetricSink + historicalSource core.HistoricalSource + gkeMetrics map[string]core.MetricDescriptor + gkeLabels map[string]core.LabelDescriptor +} + +// Create a new Api to serve from the specified cache. +func NewApi(runningInKubernetes bool, metricSink *metricsink.MetricSink, historicalSource core.HistoricalSource) *Api { + gkeMetrics := make(map[string]core.MetricDescriptor) + gkeLabels := make(map[string]core.LabelDescriptor) + for _, val := range core.StandardMetrics { + gkeMetrics[val.Name] = val.MetricDescriptor + } + for _, val := range core.LabeledMetrics { + gkeMetrics[val.Name] = val.MetricDescriptor + } + gkeMetrics[core.MetricCpuLimit.Name] = core.MetricCpuLimit.MetricDescriptor + gkeMetrics[core.MetricMemoryLimit.Name] = core.MetricMemoryLimit.MetricDescriptor + + for _, val := range core.CommonLabels() { + gkeLabels[val.Key] = val + } + for _, val := range core.ContainerLabels() { + gkeLabels[val.Key] = val + } + for _, val := range core.PodLabels() { + gkeLabels[val.Key] = val + } + + return &Api{ + runningInKubernetes: runningInKubernetes, + metricSink: metricSink, + historicalSource: historicalSource, + gkeMetrics: gkeMetrics, + gkeLabels: gkeLabels, + } +} + +// Register the mainApi on the specified endpoint. +func (a *Api) Register(container *restful.Container) { + ws := new(restful.WebService) + ws.Path("/api/v1/metric-export"). + Doc("Exports the latest point for all Heapster metrics"). + Produces(restful.MIME_JSON) + ws.Route(ws.GET(""). + To(a.exportMetrics). + Doc("export the latest data point for all metrics"). + Operation("exportMetrics"). + Writes([]*types.Timeseries{})) + container.Add(ws) + ws = new(restful.WebService) + ws.Path("/api/v1/metric-export-schema"). + Doc("Schema for metrics exported by heapster"). + Produces(restful.MIME_JSON) + ws.Route(ws.GET(""). + To(a.exportMetricsSchema). + Doc("export the schema for all metrics"). + Operation("exportmetricsSchema"). + Writes(types.TimeseriesSchema{})) + container.Add(ws) + + if a.metricSink != nil { + a.RegisterModel(container) + } + + if a.historicalSource != nil { + a.RegisterHistorical(container) + } +} + +func convertLabelDescriptor(ld core.LabelDescriptor) types.LabelDescriptor { + return types.LabelDescriptor{ + Key: ld.Key, + Description: ld.Description, + } +} + +func convertMetricDescriptor(md core.MetricDescriptor) types.MetricDescriptor { + result := types.MetricDescriptor{ + Name: md.Name, + Description: md.Description, + Labels: make([]types.LabelDescriptor, 0, len(md.Labels)), + } + for _, label := range md.Labels { + result.Labels = append(result.Labels, convertLabelDescriptor(label)) + } + + switch md.Type { + case core.MetricCumulative: + result.Type = "cumulative" + case core.MetricGauge: + result.Type = "gauge" + case core.MetricDelta: + result.Type = "delta" + } + + switch md.ValueType { + case core.ValueInt64: + result.ValueType = "int64" + case core.ValueFloat: + result.ValueType = "double" + } + + switch md.Units { + case core.UnitsBytes: + result.Units = "bytes" + case core.UnitsMilliseconds: + result.Units = "ms" + case core.UnitsNanoseconds: + result.Units = "ns" + case core.UnitsMillicores: + result.Units = "millicores" + } + return result +} + +func (a *Api) exportMetricsSchema(_ *restful.Request, response *restful.Response) { + result := types.TimeseriesSchema{ + Metrics: make([]types.MetricDescriptor, 0), + CommonLabels: make([]types.LabelDescriptor, 0), + PodLabels: make([]types.LabelDescriptor, 0), + } + for _, metric := range core.StandardMetrics { + if _, found := a.gkeMetrics[metric.Name]; found { + result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor)) + } + } + for _, metric := range core.AdditionalMetrics { + if _, found := a.gkeMetrics[metric.Name]; found { + result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor)) + } + } + for _, metric := range core.LabeledMetrics { + if _, found := a.gkeMetrics[metric.Name]; found { + result.Metrics = append(result.Metrics, convertMetricDescriptor(metric.MetricDescriptor)) + } + } + + for _, label := range core.CommonLabels() { + if _, found := a.gkeLabels[label.Key]; found { + result.CommonLabels = append(result.CommonLabels, convertLabelDescriptor(label)) + } + } + for _, label := range core.ContainerLabels() { + if _, found := a.gkeLabels[label.Key]; found { + result.CommonLabels = append(result.CommonLabels, convertLabelDescriptor(label)) + } + } + for _, label := range core.PodLabels() { + if _, found := a.gkeLabels[label.Key]; found { + result.PodLabels = append(result.PodLabels, convertLabelDescriptor(label)) + } + } + response.WriteEntity(result) +} + +func (a *Api) exportMetrics(_ *restful.Request, response *restful.Response) { + response.PrettyPrint(false) + response.WriteEntity(a.processMetricsRequest(a.metricSink.GetShortStore())) +} + +func (a *Api) processMetricsRequest(shortStorage []*core.DataBatch) []*types.Timeseries { + tsmap := make(map[string]*types.Timeseries) + + var newestBatch *core.DataBatch + for _, batch := range shortStorage { + if newestBatch == nil || newestBatch.Timestamp.Before(batch.Timestamp) { + newestBatch = batch + } + } + + var timeseries []*types.Timeseries + if newestBatch == nil { + return timeseries + } + for key, ms := range newestBatch.MetricSets { + ts := tsmap[key] + + msType := ms.Labels[core.LabelMetricSetType.Key] + + switch msType { + case core.MetricSetTypeNode, core.MetricSetTypePod, core.MetricSetTypePodContainer, core.MetricSetTypeSystemContainer: + default: + continue + } + + if ts == nil { + ts = &types.Timeseries{ + Metrics: make(map[string][]types.Point), + Labels: make(map[string]string), + } + for labelName, labelValue := range ms.Labels { + if _, ok := a.gkeLabels[labelName]; ok { + ts.Labels[labelName] = labelValue + } + } + if msType == core.MetricSetTypeNode { + ts.Labels[core.LabelContainerName.Key] = "machine" + } + if msType == core.MetricSetTypePod { + ts.Labels[core.LabelContainerName.Key] = "/pod" + } + tsmap[key] = ts + } + for metricName, metricVal := range ms.MetricValues { + if _, ok := a.gkeMetrics[metricName]; ok { + processPoint(ts, newestBatch, metricName, &metricVal, nil, ms.CreateTime) + } + } + for _, metric := range ms.LabeledMetrics { + if _, ok := a.gkeMetrics[metric.Name]; ok { + processPoint(ts, newestBatch, metric.Name, &metric.MetricValue, metric.Labels, ms.CreateTime) + } + } + } + timeseries = make([]*types.Timeseries, 0, len(tsmap)) + for _, ts := range tsmap { + timeseries = append(timeseries, ts) + } + return timeseries +} + +func processPoint(ts *types.Timeseries, db *core.DataBatch, metricName string, metricVal *core.MetricValue, labels map[string]string, creationTime time.Time) { + points := ts.Metrics[metricName] + if points == nil { + points = make([]types.Point, 0, 1) + } + point := types.Point{ + Start: db.Timestamp, + End: db.Timestamp, + } + // For cumulative metric use the provided start time. + if metricVal.MetricType == core.MetricCumulative { + point.Start = creationTime + } + var value interface{} + if metricVal.ValueType == core.ValueInt64 { + value = metricVal.IntValue + } else if metricVal.ValueType == core.ValueFloat { + value = metricVal.FloatValue + } else { + return + } + point.Value = value + if labels != nil { + point.Labels = make(map[string]string) + for key, value := range labels { + point.Labels[key] = value + } + } + points = append(points, point) + ts.Metrics[metricName] = points +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/api_test.go b/vendor/k8s.io/heapster/metrics/api/v1/api_test.go new file mode 100644 index 0000000000..1d7734bf4c --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/api_test.go @@ -0,0 +1,177 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + "testing" + "time" + + fuzz "github.com/google/gofuzz" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "k8s.io/heapster/metrics/core" + metricsink "k8s.io/heapster/metrics/sinks/metric" +) + +func TestApiFactory(t *testing.T) { + metricSink := metricsink.MetricSink{} + api := NewApi(false, &metricSink, nil) + as := assert.New(t) + for _, metric := range core.StandardMetrics { + val, exists := api.gkeMetrics[metric.Name] + as.True(exists) + as.Equal(val, metric.MetricDescriptor) + } + for _, metric := range core.LabeledMetrics { + val, exists := api.gkeMetrics[metric.Name] + as.True(exists) + as.Equal(val, metric.MetricDescriptor) + } + + for _, metric := range core.LabeledMetrics { + val, exists := api.gkeMetrics[metric.Name] + as.True(exists) + as.Equal(val, metric.MetricDescriptor) + } + labels := append(core.CommonLabels(), core.ContainerLabels()...) + labels = append(labels, core.PodLabels()...) + for _, label := range labels { + val, exists := api.gkeLabels[label.Key] + as.True(exists) + as.Equal(val, label) + } +} + +func TestFuzzInput(t *testing.T) { + api := NewApi(false, nil, nil) + data := []*core.DataBatch{} + fuzz.New().NilChance(0).Fuzz(&data) + _ = api.processMetricsRequest(data) +} + +func generateMetricSet(objectType string, labels []core.LabelDescriptor) *core.MetricSet { + ms := &core.MetricSet{ + CreateTime: time.Now().Add(-time.Hour), + ScrapeTime: time.Now(), + Labels: make(map[string]string), + MetricValues: make(map[string]core.MetricValue), + LabeledMetrics: make([]core.LabeledMetric, len(labels)), + } + // Add all necessary labels + for _, label := range labels { + ms.Labels[label.Key] = "test-value" + } + ms.Labels[core.LabelMetricSetType.Key] = objectType + // Add all standard metrics + for _, metric := range core.StandardMetrics { + ms.MetricValues[metric.Name] = core.MetricValue{ + MetricType: core.MetricCumulative, + ValueType: core.ValueInt64, + IntValue: -1, + } + } + // Add all labeled metrics + for _, metric := range core.LabeledMetrics { + lm := core.LabeledMetric{ + Name: metric.Name, + MetricValue: core.MetricValue{ + MetricType: core.MetricCumulative, + ValueType: core.ValueInt64, + IntValue: -1, + }, + Labels: make(map[string]string), + } + for _, label := range core.MetricLabels() { + lm.Labels[label.Key] = "test-value" + } + ms.LabeledMetrics = append(ms.LabeledMetrics, lm) + } + return ms +} + +func TestRealInput(t *testing.T) { + api := NewApi(false, nil, nil) + dataBatch := []*core.DataBatch{ + { + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{}, + }, + { + Timestamp: time.Now().Add(-time.Minute), + MetricSets: map[string]*core.MetricSet{}, + }, + } + labels := append(core.CommonLabels(), core.ContainerLabels()...) + labels = append(labels, core.PodLabels()...) + for _, entry := range dataBatch { + // Add a pod, container, node, systemcontainer + entry.MetricSets[core.MetricSetTypePod] = generateMetricSet(core.MetricSetTypePod, labels) + entry.MetricSets[core.MetricSetTypeNode] = generateMetricSet(core.MetricSetTypeNode, labels) + entry.MetricSets[core.MetricSetTypePodContainer] = generateMetricSet(core.MetricSetTypePodContainer, labels) + entry.MetricSets[core.MetricSetTypeSystemContainer] = generateMetricSet(core.MetricSetTypeSystemContainer, labels) + } + ts := api.processMetricsRequest(dataBatch) + type expectation struct { + count int + extraLabels bool + } + expectedMetrics := make(map[string]*expectation) + for _, metric := range core.StandardMetrics { + expectedMetrics[metric.Name] = &expectation{ + count: 4, + extraLabels: false, + } + } + for _, metric := range core.LabeledMetrics { + expectedMetrics[metric.Name] = &expectation{ + count: 4, + extraLabels: true, + } + } + as := assert.New(t) + for _, elem := range ts { + // validate labels + for _, label := range labels { + val, exists := elem.Labels[label.Key] + as.True(exists, "%q label does not exist", label.Key) + if label.Key == core.LabelMetricSetType.Key { + continue + } + if label.Key == core.LabelContainerName.Key && val != "machine" && val != "/pod" { + as.Equal(val, "test-value", "%q label's value is %q, expected 'test-value'", label.Key, val) + } + } + for mname, points := range elem.Metrics { + ex := expectedMetrics[mname] + require.NotNil(t, ex) + as.NotEqual(ex, 0) + ex.count-- + for _, point := range points { + as.Equal(point.Value, -1) + if !ex.extraLabels { + continue + } + as.Equal(len(core.MetricLabels()), len(point.Labels)) + for _, label := range core.MetricLabels() { + val, exists := point.Labels[label.Key] + as.True(exists, "expected label %q to be found - %+v", label.Key, point.Labels) + as.Equal(val, "test-value") + } + } + } + + } +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/historical_handlers.go b/vendor/k8s.io/heapster/metrics/api/v1/historical_handlers.go new file mode 100644 index 0000000000..4ce89b9d91 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/historical_handlers.go @@ -0,0 +1,848 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + "fmt" + "net/http" + "strconv" + "strings" + "time" + + restful "github.com/emicklei/go-restful" + + "k8s.io/heapster/metrics/api/v1/types" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/util/metrics" +) + +// HistoricalApi wraps the standard API to overload the fetchers to use the +// one of the persistent storage sinks instead of the in-memory metrics sink +type HistoricalApi struct { + *Api +} + +// metricsAggregationFetcher represents the capability to fetch aggregations for metrics +type metricsAggregationFetcher interface { + clusterAggregations(request *restful.Request, response *restful.Response) + nodeAggregations(request *restful.Request, response *restful.Response) + namespaceAggregations(request *restful.Request, response *restful.Response) + podAggregations(request *restful.Request, response *restful.Response) + podContainerAggregations(request *restful.Request, response *restful.Response) + freeContainerAggregations(request *restful.Request, response *restful.Response) + podListAggregations(request *restful.Request, response *restful.Response) + + isRunningInKubernetes() bool +} + +// addAggregationRoutes adds routes to a webservice which point to a metricsAggregationFetcher's methods +func addAggregationRoutes(a metricsAggregationFetcher, ws *restful.WebService) { + // The /metrics-aggregated/{aggregations}/{metric-name} endpoint exposes some aggregations for the Cluster entity of the historical API. + ws.Route(ws.GET("/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("clusterMetrics", a.clusterAggregations)). + Doc("Export some cluster-level metric aggregations"). + Operation("clusterAggregations"). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metric").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + // The /nodes/{node-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes some aggregations for a Node entity of the historical API. + // The {node-name} parameter is the hostname of a specific node. + ws.Route(ws.GET("/nodes/{node-name}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("nodeMetrics", a.nodeAggregations)). + Doc("Export a node-level metric"). + Operation("nodeAggregations"). + Param(ws.PathParameter("node-name", "The name of the node to lookup").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metric").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + if a.isRunningInKubernetes() { + // The /namespaces/{namespace-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes some aggregations + // for a Namespace entity of the historical API. + ws.Route(ws.GET("/namespaces/{namespace-name}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("namespaceMetrics", a.namespaceAggregations)). + Doc("Export some namespace-level metric aggregations"). + Operation("namespaceAggregations"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + // The /namespaces/{namespace-name}/pods/{pod-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a Pod entity of the historical API. + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podMetrics", a.podAggregations)). + Doc("Export some pod-level metric aggregations"). + Operation("podAggregations"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to lookup").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + // The /namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a Container entity of the historical API. + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podContainerMetrics", a.podContainerAggregations)). + Doc("Export some aggregations for a Pod Container"). + Operation("podContainerAggregations"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to use").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to use").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the namespace to use").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + // The /pod-id/{pod-id}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a Pod entity of the historical API. + ws.Route(ws.GET("/pod-id/{pod-id}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podMetrics", a.podAggregations)). + Doc("Export some pod-level metric aggregations"). + Operation("podAggregations"). + Param(ws.PathParameter("pod-id", "The UID of the pod to lookup").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + // The /pod-id/{pod-id}/containers/{container-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a Container entity of the historical API. + ws.Route(ws.GET("/pod-id/{pod-id}/containers/{container-name}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podContainerMetrics", a.podContainerAggregations)). + Doc("Export some aggregations for a Pod Container"). + Operation("podContainerAggregations"). + Param(ws.PathParameter("pod-id", "The name of the pod to use").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the namespace to use").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + } + + // The /nodes/{node-name}/freecontainers/{container-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a free Container entity of the historical API. + ws.Route(ws.GET("/nodes/{node-name}/freecontainers/{container-name}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("freeContainerMetrics", a.freeContainerAggregations)). + Doc("Export a contsome iner-level metric aggregations for a free container"). + Operation("freeContainerAggregations"). + Param(ws.PathParameter("node-name", "The name of the node to use").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the container to use").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResult{})) + + if a.isRunningInKubernetes() { + // The /namespaces/{namespace-name}/pod-list/{pod-list}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // metrics for a list of pods of the historical API. + ws.Route(ws.GET("/namespaces/{namespace-name}/pod-list/{pod-list}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podListAggregations", a.podListAggregations)). + Doc("Export some aggregations for all pods from the given list"). + Operation("podListAggregations"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-list", "Comma separated list of pod names to lookup").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResultList{})) + + // The /pod-id-list/{pod-id-list}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // metrics for a list of pod ids of the historical API. + ws.Route(ws.GET("/pod-id-list/{pod-id-list}/metrics-aggregated/{aggregations}/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podListAggregations", a.podListAggregations)). + Doc("Export an aggregation for all pods from the given list"). + Operation("podListAggregations"). + Param(ws.PathParameter("pod-id-list", "Comma separated list of pod UIDs to lookup").DataType("string")). + Param(ws.PathParameter("aggregations", "A comma-separated list of requested aggregations").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricAggregationResultList{})) + } +} + +// RegisterHistorical registers the Historical API endpoints. It will register the same endpoints +// as those in the model API, plus endpoints for aggregation retrieval, and endpoints to retrieve pod +// metrics by using the pod id. +func (normalApi *Api) RegisterHistorical(container *restful.Container) { + ws := new(restful.WebService) + ws.Path("/api/v1/historical"). + Doc("Root endpoint of the historical access API"). + Consumes("*/*"). + Produces(restful.MIME_JSON) + + a := &HistoricalApi{normalApi} + addClusterMetricsRoutes(a, ws) + addAggregationRoutes(a, ws) + + // register the endpoint for fetching raw metrics based on pod id + if a.isRunningInKubernetes() { + // The /pod-id/{pod-id}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a Pod entity of the historical API. + ws.Route(ws.GET("/pod-id/{pod-id}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podMetrics", a.podMetrics)). + Doc("Export some pod-level metric aggregations"). + Operation("podAggregations"). + Param(ws.PathParameter("pod-id", "The UID of the pod to lookup").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Writes(types.MetricResult{})) + + // The /pod-id/{pod-id}/containers/{container-name}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // some aggregations for a Container entity of the historical API. + ws.Route(ws.GET("/pod-id/{pod-id}/containers/{container-name}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podContainerMetrics", a.podContainerMetrics)). + Doc("Export some aggregations for a Pod Container"). + Operation("podContainerAggregations"). + Param(ws.PathParameter("pod-id", "The uid of the pod to use").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the namespace to use").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Writes(types.MetricResult{})) + + // The /pod-id-list/{pod-id-list}/metrics-aggregated/{aggregations}/{metric-name} endpoint exposes + // metrics for a list of pod ids of the historical API. + ws.Route(ws.GET("/pod-id-list/{pod-id-list}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podListAggregations", a.podListMetrics)). + Doc("Export an aggregation for all pods from the given list"). + Operation("podListAggregations"). + Param(ws.PathParameter("pod-id-list", "Comma separated list of pod UIDs to lookup").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Writes(types.MetricResultList{})) + } + + container.Add(ws) +} + +// availableClusterMetrics returns a list of available cluster metric names. +func (a *HistoricalApi) availableClusterMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ObjectType: core.MetricSetTypeCluster} + a.processMetricNamesRequest(key, response) +} + +// availableNodeMetrics returns a list of available node metric names. +func (a *HistoricalApi) availableNodeMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeNode, + NodeName: request.PathParameter("node-name"), + } + a.processMetricNamesRequest(key, response) +} + +// availableNamespaceMetrics returns a list of available namespace metric names. +func (a *HistoricalApi) availableNamespaceMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeNamespace, + NamespaceName: request.PathParameter("namespace-name"), + } + a.processMetricNamesRequest(key, response) +} + +// availablePodMetrics returns a list of available pod metric names. +func (a *HistoricalApi) availablePodMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + NamespaceName: request.PathParameter("namespace-name"), + PodName: request.PathParameter("pod-name"), + } + a.processMetricNamesRequest(key, response) +} + +// availablePodContainerMetrics returns a list of available pod container metric names. +func (a *HistoricalApi) availablePodContainerMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypePodContainer, + NamespaceName: request.PathParameter("namespace-name"), + PodName: request.PathParameter("pod-name"), + ContainerName: request.PathParameter("container-name"), + } + a.processMetricNamesRequest(key, response) +} + +// availableFreeContainerMetrics returns a list of available pod metric names. +func (a *HistoricalApi) availableFreeContainerMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeSystemContainer, + NodeName: request.PathParameter("node-name"), + ContainerName: request.PathParameter("container-name"), + } + a.processMetricNamesRequest(key, response) +} + +// nodeList lists all nodes for which we have metrics +func (a *HistoricalApi) nodeList(request *restful.Request, response *restful.Response) { + if resp, err := a.historicalSource.GetNodes(); err != nil { + response.WriteError(http.StatusInternalServerError, err) + } else { + response.WriteEntity(resp) + } +} + +// namespaceList lists all namespaces for which we have metrics +func (a *HistoricalApi) namespaceList(request *restful.Request, response *restful.Response) { + if resp, err := a.historicalSource.GetNamespaces(); err != nil { + response.WriteError(http.StatusInternalServerError, err) + } else { + response.WriteEntity(resp) + } +} + +// namespacePodList lists all pods for which we have metrics in a particular namespace +func (a *HistoricalApi) namespacePodList(request *restful.Request, response *restful.Response) { + if resp, err := a.historicalSource.GetPodsFromNamespace(request.PathParameter("namespace-name")); err != nil { + response.WriteError(http.StatusInternalServerError, err) + } else { + response.WriteEntity(resp) + } +} + +// nodeSystemContainerList lists all system containers on a node for which we have metrics +func (a *HistoricalApi) nodeSystemContainerList(request *restful.Request, response *restful.Response) { + if resp, err := a.historicalSource.GetSystemContainersFromNode(request.PathParameter("node-name")); err != nil { + response.WriteError(http.StatusInternalServerError, err) + } else { + response.WriteEntity(resp) + } +} + +// clusterMetrics returns a metric timeseries for a metric of the Cluster entity. +func (a *HistoricalApi) clusterMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ObjectType: core.MetricSetTypeCluster} + a.processMetricRequest(key, request, response) +} + +// nodeMetrics returns a metric timeseries for a metric of the Node entity. +func (a *HistoricalApi) nodeMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeNode, + NodeName: request.PathParameter("node-name"), + } + a.processMetricRequest(key, request, response) +} + +// namespaceMetrics returns a metric timeseries for a metric of the Namespace entity. +func (a *HistoricalApi) namespaceMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeNamespace, + NamespaceName: request.PathParameter("namespace-name"), + } + a.processMetricRequest(key, request, response) +} + +// podMetrics returns a metric timeseries for a metric of the Pod entity. +func (a *HistoricalApi) podMetrics(request *restful.Request, response *restful.Response) { + var key core.HistoricalKey + if request.PathParameter("pod-id") != "" { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + PodId: request.PathParameter("pod-id"), + } + } else { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + NamespaceName: request.PathParameter("namespace-name"), + PodName: request.PathParameter("pod-name"), + } + } + a.processMetricRequest(key, request, response) +} + +// freeContainerMetrics returns a metric timeseries for a metric of the Container entity. +// freeContainerMetrics addresses only free containers. +func (a *HistoricalApi) freeContainerMetrics(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeSystemContainer, + NodeName: request.PathParameter("node-name"), + ContainerName: request.PathParameter("container-name"), + } + a.processMetricRequest(key, request, response) +} + +// podListMetrics returns a list of metric timeseries for each for the listed nodes +func (a *HistoricalApi) podListMetrics(request *restful.Request, response *restful.Response) { + start, end, err := getStartEndTimeHistorical(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + + keys := []core.HistoricalKey{} + if request.PathParameter("pod-id-list") != "" { + for _, podId := range strings.Split(request.PathParameter("pod-id-list"), ",") { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + PodId: podId, + } + keys = append(keys, key) + } + } else { + for _, podName := range strings.Split(request.PathParameter("pod-list"), ",") { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + NamespaceName: request.PathParameter("namespace-name"), + PodName: podName, + } + keys = append(keys, key) + } + } + + labels, err := getLabels(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + + metricName := request.PathParameter("metric-name") + convertedMetricName := convertMetricName(metricName) + + var metrics map[core.HistoricalKey][]core.TimestampedMetricValue + if labels != nil { + metrics, err = a.historicalSource.GetLabeledMetric(convertedMetricName, labels, keys, start, end) + } else { + metrics, err = a.historicalSource.GetMetric(convertedMetricName, keys, start, end) + } + + if err != nil { + response.WriteError(http.StatusInternalServerError, err) + return + } + + result := types.MetricResultList{ + Items: make([]types.MetricResult, 0, len(keys)), + } + for _, key := range keys { + result.Items = append(result.Items, exportTimestampedMetricValue(metrics[key])) + } + response.PrettyPrint(false) + response.WriteEntity(result) +} + +// podContainerMetrics returns a metric timeseries for a metric of a Pod Container entity. +func (a *HistoricalApi) podContainerMetrics(request *restful.Request, response *restful.Response) { + var key core.HistoricalKey + if request.PathParameter("pod-id") != "" { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePodContainer, + PodId: request.PathParameter("pod-id"), + ContainerName: request.PathParameter("container-name"), + } + } else { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePodContainer, + NamespaceName: request.PathParameter("namespace-name"), + PodName: request.PathParameter("pod-name"), + ContainerName: request.PathParameter("container-name"), + } + } + a.processMetricRequest(key, request, response) +} + +// clusterAggregations returns a metric timeseries for a metric of the Cluster entity. +func (a *HistoricalApi) clusterAggregations(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ObjectType: core.MetricSetTypeCluster} + a.processAggregationRequest(key, request, response) +} + +// nodeAggregations returns a metric timeseries for a metric of the Node entity. +func (a *HistoricalApi) nodeAggregations(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeNode, + NodeName: request.PathParameter("node-name"), + } + a.processAggregationRequest(key, request, response) +} + +// namespaceAggregations returns a metric timeseries for a metric of the Namespace entity. +func (a *HistoricalApi) namespaceAggregations(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeNamespace, + NamespaceName: request.PathParameter("namespace-name"), + } + a.processAggregationRequest(key, request, response) +} + +// podAggregations returns a metric timeseries for a metric of the Pod entity. +func (a *HistoricalApi) podAggregations(request *restful.Request, response *restful.Response) { + var key core.HistoricalKey + if request.PathParameter("pod-id") != "" { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + PodId: request.PathParameter("pod-id"), + } + } else { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + NamespaceName: request.PathParameter("namespace-name"), + PodName: request.PathParameter("pod-name"), + } + } + a.processAggregationRequest(key, request, response) +} + +// podContainerAggregations returns a metric timeseries for a metric of a Pod Container entity. +func (a *HistoricalApi) podContainerAggregations(request *restful.Request, response *restful.Response) { + var key core.HistoricalKey + if request.PathParameter("pod-id") != "" { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePodContainer, + PodId: request.PathParameter("pod-id"), + ContainerName: request.PathParameter("container-name"), + } + } else { + key = core.HistoricalKey{ + ObjectType: core.MetricSetTypePodContainer, + NamespaceName: request.PathParameter("namespace-name"), + PodName: request.PathParameter("pod-name"), + ContainerName: request.PathParameter("container-name"), + } + } + a.processAggregationRequest(key, request, response) +} + +// freeContainerAggregations returns a metric timeseries for a metric of the Container entity. +// freeContainerAggregations addresses only free containers. +func (a *HistoricalApi) freeContainerAggregations(request *restful.Request, response *restful.Response) { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypeSystemContainer, + NodeName: request.PathParameter("node-name"), + ContainerName: request.PathParameter("container-name"), + } + a.processAggregationRequest(key, request, response) +} + +// podListAggregations returns a list of metric timeseries for the specified pods. +func (a *HistoricalApi) podListAggregations(request *restful.Request, response *restful.Response) { + start, end, err := getStartEndTimeHistorical(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + bucketSize, err := getBucketSize(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + aggregations, err := getAggregations(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + labels, err := getLabels(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + keys := []core.HistoricalKey{} + if request.PathParameter("pod-id-list") != "" { + for _, podId := range strings.Split(request.PathParameter("pod-id-list"), ",") { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + PodId: podId, + } + keys = append(keys, key) + } + } else { + for _, podName := range strings.Split(request.PathParameter("pod-list"), ",") { + key := core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + NamespaceName: request.PathParameter("namespace-name"), + PodName: podName, + } + keys = append(keys, key) + } + } + metricName := request.PathParameter("metric-name") + convertedMetricName := convertMetricName(metricName) + var metrics map[core.HistoricalKey][]core.TimestampedAggregationValue + if labels != nil { + metrics, err = a.historicalSource.GetLabeledAggregation(convertedMetricName, labels, aggregations, keys, start, end, bucketSize) + } else { + metrics, err = a.historicalSource.GetAggregation(convertedMetricName, aggregations, keys, start, end, bucketSize) + } + if err != nil { + response.WriteError(http.StatusInternalServerError, err) + return + } + + result := types.MetricAggregationResultList{ + Items: make([]types.MetricAggregationResult, 0, len(keys)), + } + for _, key := range keys { + result.Items = append(result.Items, exportTimestampedAggregationValue(metrics[key])) + } + response.PrettyPrint(false) + response.WriteEntity(result) +} + +// processMetricRequest retrieves a metric for the object at the requested key. +func (a *HistoricalApi) processMetricRequest(key core.HistoricalKey, request *restful.Request, response *restful.Response) { + start, end, err := getStartEndTimeHistorical(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + labels, err := getLabels(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + metricName := request.PathParameter("metric-name") + convertedMetricName := convertMetricName(metricName) + + var metrics map[core.HistoricalKey][]core.TimestampedMetricValue + if labels != nil { + metrics, err = a.historicalSource.GetLabeledMetric(convertedMetricName, labels, []core.HistoricalKey{key}, start, end) + } else { + metrics, err = a.historicalSource.GetMetric(convertedMetricName, []core.HistoricalKey{key}, start, end) + } + if err != nil { + response.WriteError(http.StatusInternalServerError, err) + return + } + + converted := exportTimestampedMetricValue(metrics[key]) + response.WriteEntity(converted) +} + +// processMetricNamesRequest retrieves the available metrics for the object at the specified key. +func (a *HistoricalApi) processMetricNamesRequest(key core.HistoricalKey, response *restful.Response) { + if resp, err := a.historicalSource.GetMetricNames(key); err != nil { + response.WriteError(http.StatusInternalServerError, err) + } else { + response.WriteEntity(resp) + } +} + +// processAggregationRequest retrieves one or more aggregations (across time) of a metric for the object specified at the given key. +func (a *HistoricalApi) processAggregationRequest(key core.HistoricalKey, request *restful.Request, response *restful.Response) { + start, end, err := getStartEndTimeHistorical(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + bucketSize, err := getBucketSize(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + aggregations, err := getAggregations(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + labels, err := getLabels(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + + metricName := request.PathParameter("metric-name") + convertedMetricName := convertMetricName(metricName) + var metrics map[core.HistoricalKey][]core.TimestampedAggregationValue + if labels != nil { + metrics, err = a.historicalSource.GetLabeledAggregation(convertedMetricName, labels, aggregations, []core.HistoricalKey{key}, start, end, bucketSize) + } else { + metrics, err = a.historicalSource.GetAggregation(convertedMetricName, aggregations, []core.HistoricalKey{key}, start, end, bucketSize) + } + if err != nil { + response.WriteError(http.StatusInternalServerError, err) + return + } + + converted := exportTimestampedAggregationValue(metrics[key]) + response.WriteEntity(converted) +} + +// getBucketSize parses the bucket size specifier into a +func getBucketSize(request *restful.Request) (time.Duration, error) { + rawSize := request.QueryParameter("bucket") + if rawSize == "" { + return 0, nil + } + + if len(rawSize) < 2 { + return 0, fmt.Errorf("unable to parse bucket size: %q is too short to be a duration", rawSize) + } + var multiplier time.Duration + var num string + + switch rawSize[len(rawSize)-1] { + case 's': + // could be s or ms + if len(rawSize) < 3 || rawSize[len(rawSize)-2] != 'm' { + multiplier = time.Second + num = rawSize[:len(rawSize)-1] + } else { + multiplier = time.Millisecond + num = rawSize[:len(rawSize)-2] + } + case 'h': + multiplier = time.Hour + num = rawSize[:len(rawSize)-1] + case 'd': + multiplier = 24 * time.Hour + num = rawSize[:len(rawSize)-1] + case 'm': + multiplier = time.Minute + num = rawSize[:len(rawSize)-1] + default: + return 0, fmt.Errorf("unable to parse bucket size: %q has no known duration suffix", rawSize) + } + + parsedNum, err := strconv.ParseUint(num, 10, 64) + if err != nil { + return 0, err + } + + return time.Duration(parsedNum) * multiplier, nil +} + +// getAggregations extracts and validates the list of requested aggregations +func getAggregations(request *restful.Request) ([]core.AggregationType, error) { + aggregationsRaw := strings.Split(request.PathParameter("aggregations"), ",") + if len(aggregationsRaw) == 0 { + return nil, fmt.Errorf("No aggregations specified") + } + + aggregations := make([]core.AggregationType, len(aggregationsRaw)) + + for ind, aggNameRaw := range aggregationsRaw { + aggName := core.AggregationType(aggNameRaw) + if _, ok := core.AllAggregations[aggName]; !ok { + return nil, fmt.Errorf("Unknown aggregation %q", aggName) + } + aggregations[ind] = aggName + } + + return aggregations, nil +} + +// exportMetricValue converts a core.MetricValue into an API MetricValue +func exportMetricValue(value *core.MetricValue) *types.MetricValue { + if value == nil { + return nil + } + + if value.ValueType == core.ValueInt64 { + return &types.MetricValue{ + IntValue: &value.IntValue, + } + } else { + floatVal := float64(value.FloatValue) + return &types.MetricValue{ + FloatValue: &floatVal, + } + } +} + +// extractMetricValue checks to see if the given metric was present in the results, and if so, +// returns it in API form +func extractMetricValue(aggregations *core.AggregationValue, aggName core.AggregationType) *types.MetricValue { + if inputVal, ok := aggregations.Aggregations[aggName]; ok { + return exportMetricValue(&inputVal) + } else { + return nil + } +} + +// exportTimestampedAggregationValue converts a core.TimestampedAggregationValue into an API MetricAggregationResult +func exportTimestampedAggregationValue(values []core.TimestampedAggregationValue) types.MetricAggregationResult { + result := types.MetricAggregationResult{ + Buckets: make([]types.MetricAggregationBucket, 0, len(values)), + BucketSize: 0, + } + for _, value := range values { + // just use the largest bucket size, since all bucket sizes should be uniform + // (except for the last one, which may be smaller) + if result.BucketSize < value.BucketSize { + result.BucketSize = value.BucketSize + } + + bucket := types.MetricAggregationBucket{ + Timestamp: value.Timestamp, + + Count: value.Count, + + Average: extractMetricValue(&value.AggregationValue, core.AggregationTypeAverage), + Maximum: extractMetricValue(&value.AggregationValue, core.AggregationTypeMaximum), + Minimum: extractMetricValue(&value.AggregationValue, core.AggregationTypeMinimum), + Median: extractMetricValue(&value.AggregationValue, core.AggregationTypeMedian), + + Percentiles: make(map[string]types.MetricValue, 3), + } + + if val, ok := value.Aggregations[core.AggregationTypePercentile50]; ok { + bucket.Percentiles["50"] = *exportMetricValue(&val) + } + if val, ok := value.Aggregations[core.AggregationTypePercentile95]; ok { + bucket.Percentiles["95"] = *exportMetricValue(&val) + } + if val, ok := value.Aggregations[core.AggregationTypePercentile99]; ok { + bucket.Percentiles["99"] = *exportMetricValue(&val) + } + + result.Buckets = append(result.Buckets, bucket) + } + return result +} + +// getStartEndTimeHistorical fetches the start and end times of the request. Unlike +// getStartEndTime, this function returns an error if the start time is not passed +// (or is zero, since many things in go use time.Time{} as an empty value) -- +// different sinks have different defaults, and certain sinks have issues if an actual +// time of zero (i.e. the epoch) is used (because that would be too many data points +// to consider in certain cases). Require applications to pass an explicit end time +// that they can deal with. +func getStartEndTimeHistorical(request *restful.Request) (time.Time, time.Time, error) { + start, end, err := getStartEndTime(request) + if err != nil { + return start, end, err + } + + if start.IsZero() { + return start, end, fmt.Errorf("no start time (or a start time of zero) provided") + } + + return start, end, err +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/historical_handlers_test.go b/vendor/k8s.io/heapster/metrics/api/v1/historical_handlers_test.go new file mode 100644 index 0000000000..62655b978c --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/historical_handlers_test.go @@ -0,0 +1,1520 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + restful "github.com/emicklei/go-restful" + "k8s.io/heapster/metrics/api/v1/types" + "k8s.io/heapster/metrics/core" +) + +type metricReq struct { + name string + keys []core.HistoricalKey + start time.Time + end time.Time + labels map[string]string + + aggregations []core.AggregationType + bucketSize time.Duration +} + +type fakeHistoricalSource struct { + metricNames map[core.HistoricalKey][]string + + nodes []string + namespaces []string + podsForNamespace map[string][]string + containersForNode map[string][]string + + metricRequests []metricReq + aggregationRequests []metricReq + + nowTime time.Time +} + +func (src *fakeHistoricalSource) GetMetric(metricName string, metricKeys []core.HistoricalKey, start, end time.Time) (map[core.HistoricalKey][]core.TimestampedMetricValue, error) { + return src.GetLabeledMetric(metricName, nil, metricKeys, start, end) +} + +func (src *fakeHistoricalSource) GetLabeledMetric(metricName string, labels map[string]string, metricKeys []core.HistoricalKey, start, end time.Time) (map[core.HistoricalKey][]core.TimestampedMetricValue, error) { + if metricName == "invalid" { + return nil, fmt.Errorf("fake error fetching metrics") + } + + src.metricRequests = append(src.metricRequests, metricReq{ + name: metricName, + keys: metricKeys, + labels: labels, + start: start, + end: end, + }) + + res := make(map[core.HistoricalKey][]core.TimestampedMetricValue, len(metricKeys)) + + for _, key := range metricKeys { + res[key] = []core.TimestampedMetricValue{ + { + Timestamp: src.nowTime.Add(-10 * time.Second), + MetricValue: core.MetricValue{ + ValueType: core.ValueFloat, + FloatValue: 33, + }, + }, + } + } + + return res, nil +} + +func (src *fakeHistoricalSource) GetAggregation(metricName string, aggregations []core.AggregationType, metricKeys []core.HistoricalKey, start, end time.Time, bucketSize time.Duration) (map[core.HistoricalKey][]core.TimestampedAggregationValue, error) { + return src.GetLabeledAggregation(metricName, nil, aggregations, metricKeys, start, end, bucketSize) +} + +func (src *fakeHistoricalSource) GetLabeledAggregation(metricName string, labels map[string]string, aggregations []core.AggregationType, metricKeys []core.HistoricalKey, start, end time.Time, bucketSize time.Duration) (map[core.HistoricalKey][]core.TimestampedAggregationValue, error) { + if metricName == "invalid" { + return nil, fmt.Errorf("fake error fetching metrics") + } + + src.metricRequests = append(src.aggregationRequests, metricReq{ + name: metricName, + keys: metricKeys, + start: start, + end: end, + labels: labels, + + aggregations: aggregations, + bucketSize: bucketSize, + }) + + res := make(map[core.HistoricalKey][]core.TimestampedAggregationValue, len(metricKeys)) + + for _, key := range metricKeys { + countVal := uint64(10) + res[key] = []core.TimestampedAggregationValue{ + { + Timestamp: src.nowTime.Add(-10 * time.Second), + BucketSize: 10 * time.Second, + AggregationValue: core.AggregationValue{ + Count: &countVal, + }, + }, + } + } + + return res, nil +} + +func (src *fakeHistoricalSource) GetMetricNames(metricKey core.HistoricalKey) ([]string, error) { + if names, ok := src.metricNames[metricKey]; ok { + return names, nil + } + + return nil, fmt.Errorf("no such object %q", metricKey.String()) +} + +func (src *fakeHistoricalSource) GetNodes() ([]string, error) { + return src.nodes, nil +} + +func (src *fakeHistoricalSource) GetNamespaces() ([]string, error) { + return src.namespaces, nil +} + +func (src *fakeHistoricalSource) GetPodsFromNamespace(namespace string) ([]string, error) { + if pods, ok := src.podsForNamespace[namespace]; ok { + return pods, nil + } + + return nil, fmt.Errorf("no such namespace %q", namespace) +} + +func (src *fakeHistoricalSource) GetSystemContainersFromNode(node string) ([]string, error) { + if conts, ok := src.containersForNode[node]; ok { + return conts, nil + } + + return nil, fmt.Errorf("no such node %q", node) +} + +func prepApi() (*HistoricalApi, *fakeHistoricalSource) { + histSrc := &fakeHistoricalSource{ + metricNames: make(map[core.HistoricalKey][]string), + } + + api := &HistoricalApi{ + Api: &Api{ + historicalSource: histSrc, + }, + } + + return api, histSrc +} + +type fakeRespRecorder struct { + headers http.Header + status int + data *bytes.Buffer +} + +func (r *fakeRespRecorder) Header() http.Header { + return r.headers +} + +func (r *fakeRespRecorder) WriteHeader(status int) { + r.status = status +} + +func (r *fakeRespRecorder) Write(content []byte) (int, error) { + return r.data.Write(content) +} + +func TestAvailableMetrics(t *testing.T) { + api, src := prepApi() + + src.metricNames = map[core.HistoricalKey][]string{ + core.HistoricalKey{ + ObjectType: core.MetricSetTypeCluster, + }: {"cm1", "cm2"}, + + core.HistoricalKey{ + ObjectType: core.MetricSetTypeNode, + NodeName: "somenode1", + }: {"nm1", "nm2"}, + + core.HistoricalKey{ + ObjectType: core.MetricSetTypeNamespace, + NamespaceName: "somens1", + }: {"nsm1", "nsm2"}, + + core.HistoricalKey{ + ObjectType: core.MetricSetTypePod, + NamespaceName: "somens1", + PodName: "somepod1", + }: {"pm1", "pm2"}, + + core.HistoricalKey{ + ObjectType: core.MetricSetTypePodContainer, + NamespaceName: "somens1", + PodName: "somepod1", + ContainerName: "somecont1", + }: {"pcm1", "pcm2"}, + + core.HistoricalKey{ + ObjectType: core.MetricSetTypeSystemContainer, + NodeName: "somenode1", + ContainerName: "somecont1", + }: {"ncm1", "ncm2"}, + } + + tests := []struct { + name string + fun func(request *restful.Request, response *restful.Response) + pathParams map[string]string + expectedNames []string + }{ + { + name: "cluster metrics", + fun: api.availableClusterMetrics, + pathParams: map[string]string{}, + expectedNames: []string{"cm1", "cm2"}, + }, + { + name: "node metrics", + fun: api.availableNodeMetrics, + pathParams: map[string]string{"node-name": "somenode1"}, + expectedNames: []string{"nm1", "nm2"}, + }, + { + name: "namespace metrics", + fun: api.availableNamespaceMetrics, + pathParams: map[string]string{"namespace-name": "somens1"}, + expectedNames: []string{"nsm1", "nsm2"}, + }, + { + name: "pod metrics", + fun: api.availablePodMetrics, + pathParams: map[string]string{ + "namespace-name": "somens1", + "pod-name": "somepod1", + }, + expectedNames: []string{"pm1", "pm2"}, + }, + { + name: "pod container metrics", + fun: api.availablePodContainerMetrics, + pathParams: map[string]string{ + "namespace-name": "somens1", + "pod-name": "somepod1", + "container-name": "somecont1", + }, + expectedNames: []string{"pcm1", "pcm2"}, + }, + { + name: "free container metrics", + fun: api.availableFreeContainerMetrics, + pathParams: map[string]string{ + "node-name": "somenode1", + "container-name": "somecont1", + }, + expectedNames: []string{"ncm1", "ncm2"}, + }, + } + + assert := assert.New(t) + restful.DefaultResponseMimeType = restful.MIME_JSON + + for _, test := range tests { + req := restful.NewRequest(&http.Request{}) + pathParams := req.PathParameters() + for k, v := range test.pathParams { + pathParams[k] = v + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + actualNames := []string{} + if err := json.Unmarshal(recorder.data.Bytes(), &actualNames); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assert.Equal(http.StatusOK, recorder.status, "status should have been OK (200)") + assert.Equal(test.expectedNames, actualNames, "should have gotten expected JSON") + } + + for _, test := range tests { + if len(test.pathParams) == 0 { + // don't test tests with no parameters for invalid parameters + continue + } + req := restful.NewRequest(&http.Request{}) + pathParams := req.PathParameters() + for k := range test.pathParams { + pathParams[k] = "some-other-value" + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + assert.Equal(http.StatusInternalServerError, recorder.status, "status should have been InternalServerError (500)") + } +} + +func TestListObjects(t *testing.T) { + api, src := prepApi() + + src.nodes = []string{"node1", "node2"} + src.namespaces = []string{"ns1", "ns2"} + src.podsForNamespace = map[string][]string{ + "ns1": {"pod1", "pod2"}, + } + src.containersForNode = map[string][]string{ + "node1": {"x/y/z", "a/b/c"}, + } + + tests := []struct { + name string + fun func(request *restful.Request, response *restful.Response) + pathParams map[string]string + expectedNames []string + }{ + { + name: "nodes", + fun: api.nodeList, + expectedNames: []string{"node1", "node2"}, + }, + { + name: "namespaces", + fun: api.namespaceList, + expectedNames: []string{"ns1", "ns2"}, + }, + { + name: "pods in namespace", + fun: api.namespacePodList, + pathParams: map[string]string{"namespace-name": "ns1"}, + expectedNames: []string{"pod1", "pod2"}, + }, + { + name: "free containers on node", + fun: api.nodeSystemContainerList, + pathParams: map[string]string{ + "node-name": "node1", + }, + expectedNames: []string{"x/y/z", "a/b/c"}, + }, + } + + assert := assert.New(t) + restful.DefaultResponseMimeType = restful.MIME_JSON + + for _, test := range tests { + req := restful.NewRequest(&http.Request{}) + pathParams := req.PathParameters() + for k, v := range test.pathParams { + pathParams[k] = v + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + actualNames := []string{} + if err := json.Unmarshal(recorder.data.Bytes(), &actualNames); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assert.Equal(http.StatusOK, recorder.status, "status should have been OK (200)") + assert.Equal(test.expectedNames, actualNames, "should have gotten expected JSON") + } + + for _, test := range tests { + if len(test.pathParams) == 0 { + // don't test tests with no parameters for invalid parameters + continue + } + req := restful.NewRequest(&http.Request{}) + pathParams := req.PathParameters() + for k := range test.pathParams { + pathParams[k] = "some-other-value" + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + assert.Equal(http.StatusInternalServerError, recorder.status, "status should have been InternalServerError (500)") + } +} + +func TestFetchMetrics(t *testing.T) { + api, src := prepApi() + nowTime := time.Now().UTC().Truncate(time.Second) + src.nowTime = nowTime + nowFunc = func() time.Time { return nowTime } + + tests := []struct { + test string + start string + end string + labels string + fun func(*restful.Request, *restful.Response) + pathParams map[string]string + expectedMetricReq metricReq + expectedStatus int + }{ + { + test: "cluster metrics", + fun: api.clusterMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeCluster}, + }, + }, + }, + { + test: "node metrics", + fun: api.nodeMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "node-name": "node1", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeNode, NodeName: "node1"}, + }, + }, + }, + { + test: "namespace metrics", + fun: api.namespaceMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeNamespace, NamespaceName: "ns1"}, + }, + }, + }, + { + test: "pod name metrics", + fun: api.podMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-name": "pod1", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, + }, + }, + }, + { + test: "pod id metrics", + fun: api.podMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id": "pod-1-id", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, PodId: "pod-1-id"}, + }, + }, + }, + { + test: "pod name container metrics", + fun: api.podContainerMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-name": "pod1", + "container-name": "cont1", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePodContainer, NamespaceName: "ns1", PodName: "pod1", ContainerName: "cont1"}, + }, + }, + }, + { + test: "pod id container metrics", + fun: api.podContainerMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id": "pod-1-id", + "container-name": "cont1", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePodContainer, PodId: "pod-1-id", ContainerName: "cont1"}, + }, + }, + }, + { + test: "system container metrics", + fun: api.freeContainerMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "node-name": "node1", + "container-name": "cont1", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeSystemContainer, NodeName: "node1", ContainerName: "cont1"}, + }, + }, + }, + { + test: "query with end", + fun: api.clusterMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + end: nowTime.Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeCluster}, + }, + start: nowTime.Add(-10 * time.Second), + end: nowTime, + }, + }, + { + test: "query with labels", + fun: api.clusterMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + labels: "somelbl:v1,otherlbl:v2.3:4", + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeCluster}, + }, + labels: map[string]string{"somelbl": "v1", "otherlbl": "v2.3:4"}, + start: nowTime.Add(-10 * time.Second), + }, + }, + { + test: "query with bad start", + fun: api.clusterMetrics, + start: "afdsfd", + expectedStatus: http.StatusBadRequest, + }, + { + test: "query with no start", + fun: api.clusterMetrics, + expectedStatus: http.StatusBadRequest, + }, + { + test: "query with error while fetching metrics", + fun: api.clusterMetrics, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "invalid", + }, + expectedStatus: http.StatusInternalServerError, + }, + { + test: "query with bad labels", + fun: api.clusterMetrics, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + labels: "abc:", + expectedStatus: http.StatusBadRequest, + }, + } + + assert := assert.New(t) + restful.DefaultResponseMimeType = restful.MIME_JSON + + // doesn't particularly correspond to the query -- we're just using it to + // test conversion between internal types + expectedNormalVals := types.MetricResult{ + LatestTimestamp: nowTime.Add(-10 * time.Second), + Metrics: []types.MetricPoint{ + { + Timestamp: nowTime.Add(-10 * time.Second), + Value: 33, + }, + }, + } + + for _, test := range tests { + queryParams := make(url.Values) + queryParams.Add("start", test.start) + queryParams.Add("end", test.end) + queryParams.Add("labels", test.labels) + u := &url.URL{RawQuery: queryParams.Encode()} + req := restful.NewRequest(&http.Request{URL: u}) + pathParams := req.PathParameters() + for k, v := range test.pathParams { + pathParams[k] = v + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + if test.expectedStatus != 0 { + assert.Equal(test.expectedStatus, recorder.status, "for test %q: status should have been an error status", test.test) + } else { + if !assert.Equal(http.StatusOK, recorder.status, "for test %q: status should have been OK (200)", test.test) { + continue + } + + actualReq := src.metricRequests[len(src.metricRequests)-1] + if test.expectedMetricReq.end.IsZero() { + test.expectedMetricReq.end = nowTime + } + assert.Equal(test.expectedMetricReq, actualReq, "for test %q: expected a different metric request to have been placed", test.test) + + actualVals := types.MetricResult{} + if err := json.Unmarshal(recorder.data.Bytes(), &actualVals); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assert.Equal(expectedNormalVals, actualVals, "for test %q: should have gotten expected JSON", test.test) + } + } + + listTests := []struct { + test string + start string + end string + labels string + fun func(*restful.Request, *restful.Response) + pathParams map[string]string + expectedMetricReq metricReq + expectedStatus int + }{ + { + test: "pod list by ids", + fun: api.podListMetrics, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id-list": "pod-id-1,pod-id-2,pod-id-3", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-1"}, + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-2"}, + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-3"}, + }, + start: nowTime.Add(-10 * time.Second), + }, + }, + { + test: "pod list by names", + fun: api.podListMetrics, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-list": "pod1,pod2,pod3", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod2"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod3"}, + }, + start: nowTime.Add(-10 * time.Second), + }, + }, + { + test: "pod list with labels", + fun: api.podListMetrics, + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id-list": "pod-id-1,pod-id-2,pod-id-3", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + labels: "somelbl:v1,otherlbl:v2.3:4", + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-1"}, + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-2"}, + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-3"}, + }, + labels: map[string]string{"somelbl": "v1", "otherlbl": "v2.3:4"}, + start: nowTime.Add(-10 * time.Second), + }, + }, + { + test: "pod list with bad start time", + fun: api.podListMetrics, + start: "afdsfd", + expectedStatus: http.StatusBadRequest, + }, + { + test: "pod list with error fetching metrics", + fun: api.podListMetrics, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "invalid", + }, + expectedStatus: http.StatusInternalServerError, + }, + { + test: "pod list with no start", + fun: api.podListMetrics, + expectedStatus: http.StatusBadRequest, + }, + { + test: "query with bad labels", + fun: api.clusterMetrics, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + labels: "abc:", + expectedStatus: http.StatusBadRequest, + }, + } + + expectedListVals := types.MetricResultList{ + Items: []types.MetricResult{ + expectedNormalVals, + expectedNormalVals, + expectedNormalVals, + }, + } + + for _, test := range listTests { + queryParams := make(url.Values) + queryParams.Add("start", test.start) + queryParams.Add("end", test.end) + queryParams.Add("labels", test.labels) + u := &url.URL{RawQuery: queryParams.Encode()} + req := restful.NewRequest(&http.Request{URL: u}) + pathParams := req.PathParameters() + for k, v := range test.pathParams { + pathParams[k] = v + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + if test.expectedStatus != 0 { + assert.Equal(test.expectedStatus, recorder.status, "for test %q: status should have been an error status", test.test) + } else { + if !assert.Equal(http.StatusOK, recorder.status, "for test %q: status should have been OK (200)", test.test) { + continue + } + + actualReq := src.metricRequests[len(src.metricRequests)-1] + if test.expectedMetricReq.end.IsZero() { + test.expectedMetricReq.end = nowTime + } + + assert.Equal(test.expectedMetricReq, actualReq, "for test %q: expected a different metric request to have been placed", test.test) + + actualVals := types.MetricResultList{} + if err := json.Unmarshal(recorder.data.Bytes(), &actualVals); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assert.Equal(expectedListVals, actualVals, "for test %q: should have gotten expected JSON", test.test) + } + } +} + +func TestFetchAggregations(t *testing.T) { + api, src := prepApi() + nowTime := time.Now().UTC().Truncate(time.Second) + src.nowTime = nowTime + nowFunc = func() time.Time { return nowTime } + + tests := []struct { + test string + bucketSize string + start string + end string + labels string + fun func(*restful.Request, *restful.Response) + pathParams map[string]string + expectedMetricReq metricReq + expectedStatus int + }{ + { + test: "cluster aggregations", + fun: api.clusterAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeCluster}, + }, + }, + }, + { + test: "node aggregations", + fun: api.nodeAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "node-name": "node1", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeNode, NodeName: "node1"}, + }, + }, + }, + { + test: "namespace aggregations", + fun: api.namespaceAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeNamespace, NamespaceName: "ns1"}, + }, + }, + }, + { + test: "pod name aggregations", + fun: api.podAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-name": "pod1", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, + }, + }, + }, + { + test: "pod id aggregations", + fun: api.podAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id": "pod-1-id", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, PodId: "pod-1-id"}, + }, + }, + }, + { + test: "pod name container aggregations", + fun: api.podContainerAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-name": "pod1", + "container-name": "cont1", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePodContainer, NamespaceName: "ns1", PodName: "pod1", ContainerName: "cont1"}, + }, + }, + }, + { + test: "pod id container aggregations", + fun: api.podContainerAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id": "pod-1-id", + "container-name": "cont1", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePodContainer, PodId: "pod-1-id", ContainerName: "cont1"}, + }, + }, + }, + { + test: "system container aggregations", + fun: api.freeContainerAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "node-name": "node1", + "container-name": "cont1", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + start: nowTime.Add(-10 * time.Second), + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeSystemContainer, NodeName: "node1", ContainerName: "cont1"}, + }, + }, + }, + { + test: "aggregations with end and bucket", + fun: api.clusterAggregations, + pathParams: map[string]string{ + "metric-name": "some-metric", + "aggregations": "count,average", + }, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + end: nowTime.Format(time.RFC3339), + bucketSize: "20s", + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeCluster}, + }, + start: nowTime.Add(-10 * time.Second), + end: nowTime, + bucketSize: 20 * time.Second, + }, + }, + { + test: "aggregations with labels", + fun: api.clusterAggregations, + pathParams: map[string]string{ + "metric-name": "some-metric", + "aggregations": "count,average", + }, + labels: "somelbl:v1,otherlbl:v2.3:4", + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypeCluster}, + }, + start: nowTime.Add(-10 * time.Second), + labels: map[string]string{"somelbl": "v1", "otherlbl": "v2.3:4"}, + }, + }, + { + test: "aggregations with bad start time", + fun: api.clusterAggregations, + start: "afdsfd", + expectedStatus: http.StatusBadRequest, + }, + { + test: "aggregations with fetch error", + fun: api.clusterAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "invalid", + "aggregations": "count,average", + }, + expectedStatus: http.StatusInternalServerError, + }, + { + test: "aggregations with no start time", + fun: api.clusterAggregations, + expectedStatus: http.StatusBadRequest, + }, + } + + assert := assert.New(t) + restful.DefaultResponseMimeType = restful.MIME_JSON + + // doesn't particularly correspond to the query -- we're just using it to + // test conversion between internal types + countVal := uint64(10) + expectedNormalVals := types.MetricAggregationResult{ + BucketSize: 10 * time.Second, + Buckets: []types.MetricAggregationBucket{ + { + Timestamp: src.nowTime.Add(-10 * time.Second), + Count: &countVal, + }, + }, + } + + aggList := []core.AggregationType{ + core.AggregationTypeCount, + core.AggregationTypeAverage, + } + + for _, test := range tests { + queryParams := make(url.Values) + queryParams.Add("start", test.start) + queryParams.Add("end", test.end) + queryParams.Add("bucket", test.bucketSize) + queryParams.Add("labels", test.labels) + u := &url.URL{RawQuery: queryParams.Encode()} + req := restful.NewRequest(&http.Request{URL: u}) + pathParams := req.PathParameters() + for k, v := range test.pathParams { + pathParams[k] = v + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + if test.expectedStatus != 0 { + assert.Equal(test.expectedStatus, recorder.status, "for test %q: status should have been an error status", test.test) + } else { + if !assert.Equal(http.StatusOK, recorder.status, "for test %q: status should have been OK (200)", test.test) { + continue + } + + actualReq := src.metricRequests[len(src.metricRequests)-1] + if test.expectedMetricReq.end.IsZero() { + test.expectedMetricReq.end = nowTime + } + + test.expectedMetricReq.aggregations = aggList + assert.Equal(test.expectedMetricReq, actualReq, "for test %q: expected a different metric request to have been placed", test.test) + + actualVals := types.MetricAggregationResult{} + if err := json.Unmarshal(recorder.data.Bytes(), &actualVals); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assert.Equal(expectedNormalVals, actualVals, "for test %q: should have gotten expected JSON", test.test) + } + } + + listTests := []struct { + test string + start string + labels string + end string + fun func(*restful.Request, *restful.Response) + pathParams map[string]string + expectedMetricReq metricReq + expectedStatus int + }{ + { + test: "pod id list aggregations", + fun: api.podListAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "pod-id-list": "pod-id-1,pod-id-2,pod-id-3", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-1"}, + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-2"}, + {ObjectType: core.MetricSetTypePod, PodId: "pod-id-3"}, + }, + start: nowTime.Add(-10 * time.Second), + }, + }, + { + test: "pod name list aggregations", + fun: api.podListAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-list": "pod1,pod2,pod3", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod2"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod3"}, + }, + start: nowTime.Add(-10 * time.Second), + }, + }, + { + test: "pod list aggregations with labels", + fun: api.podListAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + labels: "somelbl:v1,otherlbl:v2.3:4", + pathParams: map[string]string{ + "metric-name": "some-metric", + "namespace-name": "ns1", + "pod-list": "pod1,pod2,pod3", + "aggregations": "count,average", + }, + expectedMetricReq: metricReq{ + name: "some-metric", + keys: []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod1"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod2"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "ns1", PodName: "pod3"}, + }, + start: nowTime.Add(-10 * time.Second), + labels: map[string]string{"somelbl": "v1", "otherlbl": "v2.3:4"}, + }, + }, + { + test: "pod list aggregations with bad start time", + fun: api.podListAggregations, + start: "afdsfd", + expectedStatus: http.StatusBadRequest, + }, + { + test: "pod list aggregations with fetch error", + fun: api.podListAggregations, + start: nowTime.Add(-10 * time.Second).Format(time.RFC3339), + pathParams: map[string]string{ + "metric-name": "invalid", + "aggregations": "count,average", + }, + expectedStatus: http.StatusInternalServerError, + }, + { + test: "pod list aggregations with no start time", + fun: api.podListAggregations, + expectedStatus: http.StatusBadRequest, + }, + } + + expectedListVals := types.MetricAggregationResultList{ + Items: []types.MetricAggregationResult{ + expectedNormalVals, + expectedNormalVals, + expectedNormalVals, + }, + } + + for _, test := range listTests { + queryParams := make(url.Values) + queryParams.Add("start", test.start) + queryParams.Add("end", test.end) + queryParams.Add("labels", test.labels) + u := &url.URL{RawQuery: queryParams.Encode()} + req := restful.NewRequest(&http.Request{URL: u}) + pathParams := req.PathParameters() + for k, v := range test.pathParams { + pathParams[k] = v + } + recorder := &fakeRespRecorder{ + data: new(bytes.Buffer), + headers: make(http.Header), + } + resp := restful.NewResponse(recorder) + + test.fun(req, resp) + + if test.expectedStatus != 0 { + assert.Equal(test.expectedStatus, recorder.status, "for test %q: status should have been an error status", test.test) + } else { + if !assert.Equal(http.StatusOK, recorder.status, "for test %q: status should have been OK (200)", test.test) { + continue + } + + actualReq := src.metricRequests[len(src.metricRequests)-1] + if test.expectedMetricReq.end.IsZero() { + test.expectedMetricReq.end = nowTime + } + test.expectedMetricReq.aggregations = aggList + assert.Equal(test.expectedMetricReq, actualReq, "for test %q: expected a different metric request to have been placed", test.test) + + actualVals := types.MetricAggregationResultList{} + if err := json.Unmarshal(recorder.data.Bytes(), &actualVals); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + assert.Equal(expectedListVals, actualVals, "for test %q: should have gotten expected JSON", test.test) + } + } +} + +func TestGetBucketSize(t *testing.T) { + tests := []struct { + sizeParam string + expectedDuration time.Duration + expectedError bool + }{ + { + // empty duration + sizeParam: "", + expectedDuration: 0, + }, + { + // no units + sizeParam: "1", + expectedError: true, + }, + { + // unknown unit + sizeParam: "1g", + expectedError: true, + }, + { + // invalid unit (looks like ms) + sizeParam: "10gs", + expectedError: true, + }, + { + // NaN + sizeParam: "abch", + expectedError: true, + }, + { + sizeParam: "5ms", + expectedDuration: 5 * time.Millisecond, + }, + { + sizeParam: "10s", + expectedDuration: 10 * time.Second, + }, + { + sizeParam: "15m", + expectedDuration: 15 * time.Minute, + }, + { + sizeParam: "20h", + expectedDuration: 20 * time.Hour, + }, + { + sizeParam: "25d", + expectedDuration: 600 * time.Hour, + }, + } + + assert := assert.New(t) + for _, test := range tests { + u, err := url.Parse(fmt.Sprintf("/foo?bucket=%s", test.sizeParam)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + req := restful.NewRequest(&http.Request{URL: u}) + res, err := getBucketSize(req) + if test.expectedError { + assert.Error(err, fmt.Sprintf("%q should have been an invalid value", test.sizeParam)) + } else if assert.NoError(err, fmt.Sprintf("%q should have been a valid value", test.sizeParam)) { + assert.Equal(test.expectedDuration, res, fmt.Sprintf("%q should have given the correct duration", test.sizeParam)) + } + } +} + +func TestGetAggregations(t *testing.T) { + assert := assert.New(t) + + validAggregations := "average,max" + invalidAggregations := "max,non-existant" + + req := restful.NewRequest(&http.Request{}) + pathParams := req.PathParameters() + pathParams["aggregations"] = validAggregations + + validRes, validErr := getAggregations(req) + if assert.NoError(validErr, "expected valid list to not produce an error") { + assert.Equal([]core.AggregationType{core.AggregationTypeAverage, core.AggregationTypeMaximum}, validRes, "expected valid list to be properly split and converted to AggregationType values") + } + + pathParams["aggregations"] = invalidAggregations + _, invalidErr := getAggregations(req) + assert.Error(invalidErr, "expected list with unknown aggregations to produce an error") +} + +func TestExportMetricValue(t *testing.T) { + assert := assert.New(t) + + assert.Nil(exportMetricValue(nil), "a nil input value should yield a nil output value") + + intVal := &core.MetricValue{ + IntValue: 33, + ValueType: core.ValueInt64, + } + outputIntVal := exportMetricValue(intVal) + if assert.NotNil(outputIntVal, "a non-nil input should yield a non-nil output") && assert.NotNil(outputIntVal.IntValue, "an int-valued input should yield an output with the IntValue set") { + assert.Equal(intVal.IntValue, *outputIntVal.IntValue, "the input int value should be the same as the output int value") + } + + floatVal := &core.MetricValue{ + FloatValue: 66.0, + ValueType: core.ValueFloat, + } + outputFloatVal := exportMetricValue(floatVal) + if assert.NotNil(outputFloatVal, "a non-nil input should yield a non-nil output") && assert.NotNil(outputFloatVal.FloatValue, "an float-valued input should yield an output with the FloatValue set") { + assert.Equal(float64(floatVal.FloatValue), *outputFloatVal.FloatValue, "the input float value should be the same as the output float value (as a float64)") + } +} + +func TestExtractMetricValue(t *testing.T) { + assert := assert.New(t) + + aggregationVal := &core.AggregationValue{ + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: { + FloatValue: 66.0, + ValueType: core.ValueFloat, + }, + }, + } + + avgRes := extractMetricValue(aggregationVal, core.AggregationTypeAverage) + if assert.NotNil(avgRes, "a present aggregation should yield a non-nil output") && assert.NotNil(avgRes.FloatValue, "the output float value should be set when the input aggregation value is of the float type") { + assert.Equal(66.0, *avgRes.FloatValue, "the output float value should be the same as the aggregation's float value") + } + + maxRes := extractMetricValue(aggregationVal, core.AggregationTypeMaximum) + assert.Nil(maxRes, "a non-present aggregation should yield a nil output") +} + +func TestExportTimestampedAggregationValue(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + nowTime := time.Now().UTC() + + count10 := uint64(10) + count11 := uint64(11) + values := []core.TimestampedAggregationValue{ + { + Timestamp: nowTime.Add(-20 * time.Second), + BucketSize: 10 * time.Second, + AggregationValue: core.AggregationValue{ + Count: &count10, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: { + FloatValue: 66.0, + ValueType: core.ValueFloat, + }, + core.AggregationTypePercentile50: { + IntValue: 44, + ValueType: core.ValueInt64, + }, + }, + }, + }, + { + Timestamp: nowTime.Add(-10 * time.Second), + BucketSize: 10 * time.Second, + AggregationValue: core.AggregationValue{ + Count: &count11, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: { + FloatValue: 88.0, + ValueType: core.ValueFloat, + }, + core.AggregationTypePercentile50: { + IntValue: 55, + ValueType: core.ValueInt64, + }, + }, + }, + }, + } + + res := exportTimestampedAggregationValue(values) + + assert.Equal(10*time.Second, res.BucketSize, "the output bucket size should be 10s") + require.Equal(len(values), len(res.Buckets), "there should be an output bucket for every input value") + + for i, bucket := range res.Buckets { + inputVal := values[i] + + assert.Equal(inputVal.Count, bucket.Count, "the output bucket should have the same sample count as the input value") + + if assert.NotNil(bucket.Average, "the output bucket should have the average set") { + metricVal := inputVal.Aggregations[core.AggregationTypeAverage] + assert.Equal(exportMetricValue(&metricVal), bucket.Average, "the output bucket should have an average value equal to that of the input value") + } + + percVal, ok := bucket.Percentiles["50"] + if assert.True(ok, "the output bucket should have the 50th percentile set") { + metricVal := inputVal.Aggregations[core.AggregationTypePercentile50] + assert.Equal(exportMetricValue(&metricVal), &percVal, "the output bucket should have a 50th-percentile value equal to that of the input value") + } + } +} + +func TestGetLabels(t *testing.T) { + assert := assert.New(t) + + tests := []struct { + test string + input string + outputVal map[string]string + outputError bool + }{ + { + test: "working labels", + input: "k1:v1,k2:v2.3:4+5", + outputVal: map[string]string{"k1": "v1", "k2": "v2.3:4+5"}, + }, + { + test: "bad label (no separator)", + input: "k1,k2:v2", + outputError: true, + }, + { + test: "bad label (no key)", + input: "k1:v1,:v2", + outputError: true, + }, + { + test: "bad label (no value)", + input: "k1:v1,k1:", + outputError: true, + }, + { + test: "empty", + input: "", + outputVal: nil, + }, + } + + for _, test := range tests { + queryParams := make(url.Values) + queryParams.Add("labels", test.input) + u := &url.URL{RawQuery: queryParams.Encode()} + req := restful.NewRequest(&http.Request{URL: u}) + res, err := getLabels(req) + if test.outputError && !assert.Error(err, "test %q should have yielded an error", test.test) { + continue + } else if !test.outputError && !assert.NoError(err, "test %q should not have yielded an error", test.test) { + continue + } + + assert.Equal(test.outputVal, res, "test %q should have output the correct label map", test.test) + } +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/model_handlers.go b/vendor/k8s.io/heapster/metrics/api/v1/model_handlers.go new file mode 100644 index 0000000000..f945062ed2 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/model_handlers.go @@ -0,0 +1,514 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + "errors" + "fmt" + "net/http" + "strings" + "time" + + restful "github.com/emicklei/go-restful" + + "k8s.io/heapster/metrics/api/v1/types" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/util/metrics" +) + +// for testing +var nowFunc = time.Now + +// errModelNotActivated is the error that is returned by the API handlers +// when manager.model has not been initialized. +var errModelNotActivated = errors.New("the model is not activated") + +// Deprecated - clients should switch to full metric names ASAP. +var deprecatedMetricNamesConversion = map[string]string{ + "cpu-usage": "cpu/usage_rate", + "cpu-limit": "cpu/limit", + "memory-limit": "memory/limit", + "memory-usage": "memory/usage", + "memory-working": "memory/working_set", +} + +type clusterMetricsFetcher interface { + availableClusterMetrics(request *restful.Request, response *restful.Response) + clusterMetrics(request *restful.Request, response *restful.Response) + + nodeList(request *restful.Request, response *restful.Response) + availableNodeMetrics(request *restful.Request, response *restful.Response) + nodeMetrics(request *restful.Request, response *restful.Response) + + namespaceList(request *restful.Request, response *restful.Response) + availableNamespaceMetrics(request *restful.Request, response *restful.Response) + namespaceMetrics(request *restful.Request, response *restful.Response) + + namespacePodList(request *restful.Request, response *restful.Response) + availablePodMetrics(request *restful.Request, response *restful.Response) + podMetrics(request *restful.Request, response *restful.Response) + + availablePodContainerMetrics(request *restful.Request, response *restful.Response) + podContainerMetrics(request *restful.Request, response *restful.Response) + + nodeSystemContainerList(request *restful.Request, response *restful.Response) + availableFreeContainerMetrics(request *restful.Request, response *restful.Response) + freeContainerMetrics(request *restful.Request, response *restful.Response) + + podListMetrics(request *restful.Request, response *restful.Response) + + isRunningInKubernetes() bool +} + +// addClusterMetricsRoutes adds all the standard model routes to a WebService. +// It should already have a base path registered. +func addClusterMetricsRoutes(a clusterMetricsFetcher, ws *restful.WebService) { + // The /metrics/ endpoint returns a list of all available metrics for the Cluster entity of the model. + ws.Route(ws.GET("/metrics/"). + To(metrics.InstrumentRouteFunc("availableClusterMetrics", a.availableClusterMetrics)). + Doc("Get a list of all available metrics for the Cluster entity"). + Operation("availableClusterMetrics")) + + // The /metrics/{metric-name} endpoint exposes an aggregated metric for the Cluster entity of the model. + ws.Route(ws.GET("/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("clusterMetrics", a.clusterMetrics)). + Doc("Export an aggregated cluster-level metric"). + Operation("clusterMetrics"). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metric").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + + // The /nodes/{node-name}/metrics endpoint returns a list of all nodes with some metrics. + ws.Route(ws.GET("/nodes/"). + To(metrics.InstrumentRouteFunc("nodeList", a.nodeList)). + Doc("Get a list of all nodes that have some current metrics"). + Operation("nodeList")) + + // The /nodes/{node-name}/metrics endpoint returns a list of all available metrics for a Node entity. + ws.Route(ws.GET("/nodes/{node-name}/metrics/"). + To(metrics.InstrumentRouteFunc("availableNodeMetrics", a.availableNodeMetrics)). + Doc("Get a list of all available metrics for a Node entity"). + Operation("availableNodeMetrics"). + Param(ws.PathParameter("node-name", "The name of the node to lookup").DataType("string"))) + + // The /nodes/{node-name}/metrics/{metric-name} endpoint exposes a metric for a Node entity of the model. + // The {node-name} parameter is the hostname of a specific node. + ws.Route(ws.GET("/nodes/{node-name}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("nodeMetrics", a.nodeMetrics)). + Doc("Export a node-level metric"). + Operation("nodeMetrics"). + Param(ws.PathParameter("node-name", "The name of the node to lookup").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metric").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + + if a.isRunningInKubernetes() { + + ws.Route(ws.GET("/namespaces/"). + To(metrics.InstrumentRouteFunc("namespaceList", a.namespaceList)). + Doc("Get a list of all namespaces that have some current metrics"). + Operation("namespaceList")) + + // The /namespaces/{namespace-name}/metrics endpoint returns a list of all available metrics for a Namespace entity. + ws.Route(ws.GET("/namespaces/{namespace-name}/metrics"). + To(metrics.InstrumentRouteFunc("availableNamespaceMetrics", a.availableNamespaceMetrics)). + Doc("Get a list of all available metrics for a Namespace entity"). + Operation("availableNamespaceMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string"))) + + // The /namespaces/{namespace-name}/metrics/{metric-name} endpoint exposes an aggregated metrics + // for a Namespace entity of the model. + ws.Route(ws.GET("/namespaces/{namespace-name}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("namespaceMetrics", a.namespaceMetrics)). + Doc("Export an aggregated namespace-level metric"). + Operation("namespaceMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/"). + To(metrics.InstrumentRouteFunc("namespacePodList", a.namespacePodList)). + Doc("Get a list of pods from the given namespace that have some metrics"). + Operation("namespacePodList"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string"))) + + // The /namespaces/{namespace-name}/pods/{pod-name}/metrics endpoint returns a list of all available metrics for a Pod entity. + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/metrics"). + To(metrics.InstrumentRouteFunc("availablePodMetrics", a.availablePodMetrics)). + Doc("Get a list of all available metrics for a Pod entity"). + Operation("availablePodMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to lookup").DataType("string"))) + + // The /namespaces/{namespace-name}/pods/{pod-name}/metrics/{metric-name} endpoint exposes + // an aggregated metric for a Pod entity of the model. + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podMetrics", a.podMetrics)). + Doc("Export an aggregated pod-level metric"). + Operation("podMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to lookup").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + + // The /namespaces/{namespace-name}/pods/{pod-name}/containers/metrics/{container-name}/metrics endpoint + // returns a list of all available metrics for a Pod Container entity. + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics"). + To(metrics.InstrumentRouteFunc("availableContainerMetrics", a.availablePodContainerMetrics)). + Doc("Get a list of all available metrics for a Pod entity"). + Operation("availableContainerMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to lookup").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the namespace to use").DataType("string"))) + + // The /namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics/{metric-name} endpoint exposes + // a metric for a Container entity of the model. + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/containers/{container-name}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podContainerMetrics", a.podContainerMetrics)). + Doc("Export an aggregated metric for a Pod Container"). + Operation("podContainerMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to use").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to use").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the namespace to use").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + } + + ws.Route(ws.GET("/nodes/{node-name}/freecontainers/"). + To(metrics.InstrumentRouteFunc("systemContainerList", a.nodeSystemContainerList)). + Doc("Get a list of all non-pod containers with some metrics"). + Operation("systemContainerList"). + Param(ws.PathParameter("node-name", "The name of the namespace to lookup").DataType("string"))) + + // The /nodes/{node-name}/freecontainers/{container-name}/metrics endpoint + // returns a list of all available metrics for a Free Container entity. + ws.Route(ws.GET("/nodes/{node-name}/freecontainers/{container-name}/metrics"). + To(metrics.InstrumentRouteFunc("availableMetrics", a.availableFreeContainerMetrics)). + Doc("Get a list of all available metrics for a free Container entity"). + Operation("availableMetrics"). + Param(ws.PathParameter("node-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the namespace to use").DataType("string"))) + + // The /nodes/{node-name}/freecontainers/{container-name}/metrics/{metric-name} endpoint exposes + // a metric for a free Container entity of the model. + ws.Route(ws.GET("/nodes/{node-name}/freecontainers/{container-name}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("freeContainerMetrics", a.freeContainerMetrics)). + Doc("Export a container-level metric for a free container"). + Operation("freeContainerMetrics"). + Param(ws.PathParameter("node-name", "The name of the node to use").DataType("string")). + Param(ws.PathParameter("container-name", "The name of the container to use").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + + if a.isRunningInKubernetes() { + // The /namespaces/{namespace-name}/pod-list/{pod-list}/metrics/{metric-name} endpoint exposes + // metrics for a list od pods of the model. + ws.Route(ws.GET("/namespaces/{namespace-name}/pod-list/{pod-list}/metrics/{metric-name:*}"). + To(metrics.InstrumentRouteFunc("podListMetric", a.podListMetrics)). + Doc("Export a metric for all pods from the given list"). + Operation("podListMetric"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-list", "Comma separated list of pod names to lookup").DataType("string")). + Param(ws.PathParameter("metric-name", "The name of the requested metric").DataType("string")). + Param(ws.QueryParameter("start", "Start time for requested metrics").DataType("string")). + Param(ws.QueryParameter("end", "End time for requested metric").DataType("string")). + Param(ws.QueryParameter("labels", "A comma-separated list of key:values pairs to use to search for a labeled metric").DataType("string")). + Writes(types.MetricResult{})) + } +} + +func (a *Api) isRunningInKubernetes() bool { + return a.runningInKubernetes +} + +// RegisterModel registers the Model API endpoints. +// All endpoints that end with a {metric-name} also receive a start time query parameter. +// The start and end times should be specified as a string, formatted according to RFC 3339. +func (a *Api) RegisterModel(container *restful.Container) { + ws := new(restful.WebService) + ws.Path("/api/v1/model"). + Doc("Root endpoint of the stats model"). + Consumes("*/*"). + Produces(restful.MIME_JSON) + + addClusterMetricsRoutes(a, ws) + + ws.Route(ws.GET("/debug/allkeys"). + To(metrics.InstrumentRouteFunc("debugAllKeys", a.allKeys)). + Doc("Get keys of all metric sets available"). + Operation("debugAllKeys")) + container.Add(ws) +} + +// availableMetrics returns a list of available cluster metric names. +func (a *Api) availableClusterMetrics(request *restful.Request, response *restful.Response) { + a.processMetricNamesRequest(core.ClusterKey(), response) +} + +// availableMetrics returns a list of available node metric names. +func (a *Api) availableNodeMetrics(request *restful.Request, response *restful.Response) { + a.processMetricNamesRequest(core.NodeKey(request.PathParameter("node-name")), response) +} + +// availableMetrics returns a list of available namespace metric names. +func (a *Api) availableNamespaceMetrics(request *restful.Request, response *restful.Response) { + a.processMetricNamesRequest(core.NamespaceKey(request.PathParameter("namespace-name")), response) +} + +// availableMetrics returns a list of available pod metric names. +func (a *Api) availablePodMetrics(request *restful.Request, response *restful.Response) { + a.processMetricNamesRequest( + core.PodKey(request.PathParameter("namespace-name"), + request.PathParameter("pod-name")), response) +} + +// availableMetrics returns a list of available pod metric names. +func (a *Api) availablePodContainerMetrics(request *restful.Request, response *restful.Response) { + a.processMetricNamesRequest( + core.PodContainerKey(request.PathParameter("namespace-name"), + request.PathParameter("pod-name"), + request.PathParameter("container-name"), + ), response) +} + +// availableMetrics returns a list of available pod metric names. +func (a *Api) availableFreeContainerMetrics(request *restful.Request, response *restful.Response) { + a.processMetricNamesRequest( + core.NodeContainerKey(request.PathParameter("node-name"), + request.PathParameter("container-name"), + ), response) +} + +func (a *Api) nodeList(request *restful.Request, response *restful.Response) { + response.WriteEntity(a.metricSink.GetNodes()) +} + +func (a *Api) namespaceList(request *restful.Request, response *restful.Response) { + response.WriteEntity(a.metricSink.GetNamespaces()) +} + +func (a *Api) namespacePodList(request *restful.Request, response *restful.Response) { + response.WriteEntity(a.metricSink.GetPodsFromNamespace(request.PathParameter("namespace-name"))) +} + +func (a *Api) nodeSystemContainerList(request *restful.Request, response *restful.Response) { + response.WriteEntity(a.metricSink.GetSystemContainersFromNode(request.PathParameter("node-name"))) +} + +func (a *Api) allKeys(request *restful.Request, response *restful.Response) { + response.WriteEntity(a.metricSink.GetMetricSetKeys()) +} + +// clusterMetrics returns a metric timeseries for a metric of the Cluster entity. +func (a *Api) clusterMetrics(request *restful.Request, response *restful.Response) { + a.processMetricRequest(core.ClusterKey(), request, response) +} + +// nodeMetrics returns a metric timeseries for a metric of the Node entity. +func (a *Api) nodeMetrics(request *restful.Request, response *restful.Response) { + a.processMetricRequest(core.NodeKey(request.PathParameter("node-name")), + request, response) +} + +// namespaceMetrics returns a metric timeseries for a metric of the Namespace entity. +func (a *Api) namespaceMetrics(request *restful.Request, response *restful.Response) { + a.processMetricRequest(core.NamespaceKey(request.PathParameter("namespace-name")), + request, response) +} + +// podMetrics returns a metric timeseries for a metric of the Pod entity. +func (a *Api) podMetrics(request *restful.Request, response *restful.Response) { + a.processMetricRequest( + core.PodKey(request.PathParameter("namespace-name"), + request.PathParameter("pod-name")), + request, response) +} + +func (a *Api) podListMetrics(request *restful.Request, response *restful.Response) { + start, end, err := getStartEndTime(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + ns := request.PathParameter("namespace-name") + keys := []string{} + metricName := request.PathParameter("metric-name") + convertedMetricName := convertMetricName(metricName) + for _, podName := range strings.Split(request.PathParameter("pod-list"), ",") { + keys = append(keys, core.PodKey(ns, podName)) + } + + labels, err := getLabels(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + + var metrics map[string][]core.TimestampedMetricValue + if labels != nil { + metrics = a.metricSink.GetLabeledMetric(convertedMetricName, labels, keys, start, end) + } else { + metrics = a.metricSink.GetMetric(convertedMetricName, keys, start, end) + } + + result := types.MetricResultList{ + Items: make([]types.MetricResult, 0, len(keys)), + } + for _, key := range keys { + result.Items = append(result.Items, exportTimestampedMetricValue(metrics[key])) + } + response.PrettyPrint(false) + response.WriteEntity(result) +} + +// podContainerMetrics returns a metric timeseries for a metric of a Pod Container entity. +// podContainerMetrics uses the namespace-name/pod-name/container-name path. +func (a *Api) podContainerMetrics(request *restful.Request, response *restful.Response) { + a.processMetricRequest( + core.PodContainerKey(request.PathParameter("namespace-name"), + request.PathParameter("pod-name"), + request.PathParameter("container-name"), + ), + request, response) +} + +// freeContainerMetrics returns a metric timeseries for a metric of the Container entity. +// freeContainerMetrics addresses only free containers, by using the node-name/container-name path. +func (a *Api) freeContainerMetrics(request *restful.Request, response *restful.Response) { + a.processMetricRequest( + core.NodeContainerKey(request.PathParameter("node-name"), + request.PathParameter("container-name"), + ), + request, response) +} + +// parseRequestParam parses a time.Time from a named QueryParam, using the RFC3339 format. +func parseTimeParam(queryParam string, defaultValue time.Time) (time.Time, error) { + if queryParam != "" { + reqStamp, err := time.Parse(time.RFC3339, queryParam) + if err != nil { + return time.Time{}, fmt.Errorf("timestamp argument cannot be parsed: %s", err) + } + return reqStamp, nil + } + return defaultValue, nil +} + +func (a *Api) processMetricRequest(key string, request *restful.Request, response *restful.Response) { + start, end, err := getStartEndTime(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + metricName := request.PathParameter("metric-name") + convertedMetricName := convertMetricName(metricName) + labels, err := getLabels(request) + if err != nil { + response.WriteError(http.StatusBadRequest, err) + return + } + + var metrics map[string][]core.TimestampedMetricValue + if labels != nil { + metrics = a.metricSink.GetLabeledMetric(convertedMetricName, labels, []string{key}, start, end) + } else { + metrics = a.metricSink.GetMetric(convertedMetricName, []string{key}, start, end) + } + converted := exportTimestampedMetricValue(metrics[key]) + response.WriteEntity(converted) +} + +func (a *Api) processMetricNamesRequest(key string, response *restful.Response) { + metricNames := a.metricSink.GetMetricNames(key) + response.WriteEntity(metricNames) +} + +func convertMetricName(metricName string) string { + if convertedMetricName, ok := deprecatedMetricNamesConversion[metricName]; ok { + return convertedMetricName + } + return metricName +} + +func getStartEndTime(request *restful.Request) (time.Time, time.Time, error) { + start, err := parseTimeParam(request.QueryParameter("start"), time.Time{}) + if err != nil { + return time.Time{}, time.Time{}, err + } + end, err := parseTimeParam(request.QueryParameter("end"), nowFunc()) + if err != nil { + return time.Time{}, time.Time{}, err + } + return start, end, nil +} + +func exportTimestampedMetricValue(values []core.TimestampedMetricValue) types.MetricResult { + result := types.MetricResult{ + Metrics: make([]types.MetricPoint, 0, len(values)), + } + for _, value := range values { + if result.LatestTimestamp.Before(value.Timestamp) { + result.LatestTimestamp = value.Timestamp + } + // TODO: clean up types in model api + var intValue int64 + if value.ValueType == core.ValueInt64 { + intValue = value.IntValue + } else { + intValue = int64(value.FloatValue) + } + + result.Metrics = append(result.Metrics, types.MetricPoint{ + Timestamp: value.Timestamp, + Value: uint64(intValue), + }) + } + return result +} + +func getLabels(request *restful.Request) (map[string]string, error) { + labelsRaw := request.QueryParameter("labels") + if labelsRaw == "" { + return nil, nil + } + + kvPairs := strings.Split(labelsRaw, ",") + labels := make(map[string]string, len(kvPairs)) + for _, kvPair := range kvPairs { + kvSplit := strings.SplitN(kvPair, ":", 2) + if len(kvSplit) != 2 || kvSplit[0] == "" || kvSplit[1] == "" { + return nil, fmt.Errorf("invalid label pair %q", kvPair) + } + labels[kvSplit[0]] = kvSplit[1] + } + + return labels, nil +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/types/historical_types.go b/vendor/k8s.io/heapster/metrics/api/v1/types/historical_types.go new file mode 100644 index 0000000000..5da862e8ec --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/types/historical_types.go @@ -0,0 +1,49 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "time" +) + +// MetricValue is either a floating point value or an unsigned integer value +type MetricValue struct { + IntValue *int64 `json:"intValue,omitempty"` + FloatValue *float64 `json:"floatValue,omitempty"` +} + +// MetricAggregationBucket holds information about various aggregations across a single bucket of time +type MetricAggregationBucket struct { + Timestamp time.Time `json:"timestamp"` + Count *uint64 `json:"count,omitempty"` + + Average *MetricValue `json:"average,omitempty"` + Maximum *MetricValue `json:"maximum,omitempty"` + Minimum *MetricValue `json:"minimum,omitempty"` + Median *MetricValue `json:"median,omitempty"` + + Percentiles map[string]MetricValue `json:"percentiles,omitempty"` +} + +// MetricAggregationResult holds a series of MetricAggregationBuckets of a particular size +type MetricAggregationResult struct { + Buckets []MetricAggregationBucket `json:"buckets"` + BucketSize time.Duration `json:"bucketSize"` +} + +// MetricAggregationResultList is a list of MetricAggregationResults, each for a different object +type MetricAggregationResultList struct { + Items []MetricAggregationResult `json:"items"` +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/types/model_types.go b/vendor/k8s.io/heapster/metrics/api/v1/types/model_types.go new file mode 100644 index 0000000000..7eea8f2199 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/types/model_types.go @@ -0,0 +1,63 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "time" +) + +type MetricPoint struct { + Timestamp time.Time `json:"timestamp"` + Value uint64 `json:"value"` + // This will be populated only for float custom metrics. In that case + // "value" will be zero. This is a temporary hack. Overall most likely + // we will need a new api versioned in the similar way as K8S api. + FloatValue *float64 `json:"floatValue,omitempty"` +} + +type MetricResult struct { + Metrics []MetricPoint `json:"metrics"` + LatestTimestamp time.Time `json:"latestTimestamp"` +} + +type MetricResultList struct { + Items []MetricResult `json:"items"` +} + +type Stats struct { + Average uint64 `json:"average"` + NinetyFifth uint64 `json:"percentile"` + Max uint64 `json:"max"` +} + +type ExternalStatBundle struct { + Minute Stats `json:"minute"` + Hour Stats `json:"hour"` + Day Stats `json:"day"` +} + +type StatsResponse struct { + // Uptime is in seconds + Uptime uint64 `json:"uptime"` + Stats map[string]ExternalStatBundle `json:"stats"` +} + +// An ExternalEntityListEntry represents the latest CPU and Memory usage of a model entity. +// A model entity can be a Pod, a Container, a Namespace or a Node. +type ExternalEntityListEntry struct { + Name string `json:"name"` + CPUUsage uint64 `json:"cpuUsage"` + MemUsage uint64 `json:"memUsage"` +} diff --git a/vendor/k8s.io/heapster/metrics/api/v1/types/types.go b/vendor/k8s.io/heapster/metrics/api/v1/types/types.go new file mode 100644 index 0000000000..741e45960d --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/api/v1/types/types.go @@ -0,0 +1,83 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "time" +) + +// Timeseries represents a set of metrics for the same target object +// (typically a container). +type Timeseries struct { + // Map of metric names to their values. + Metrics map[string][]Point `json:"metrics"` + + // Common labels for all metrics. + Labels map[string]string `json:"labels,omitempty"` +} + +// Point represent a metric value. +type Point struct { + // The start and end time for which this data is representative. + Start time.Time `json:"start"` + End time.Time `json:"end"` + + // Labels specific to this data point. + Labels map[string]string `json:"labels,omitempty"` + + // The value of the metric. + Value interface{} `json:"value"` +} + +// TimeseriesSchema represents all the metrics and labels. +type TimeseriesSchema struct { + // All the metrics handled by heapster. + Metrics []MetricDescriptor `json:"metrics,omitempty"` + // Labels that are common to all metrics. + CommonLabels []LabelDescriptor `json:"common_labels,omitempty"` + // Labels that are present only for containers in pods. + // A container metric belongs to a pod is "pod_name" label is set. + PodLabels []LabelDescriptor `json:"pod_labels,omitempty"` +} + +// To maintain stable api for GKE. + +type MetricDescriptor struct { + // The unique name of the metric. + Name string `json:"name,omitempty"` + + // Description of the metric. + Description string `json:"description,omitempty"` + + // Descriptor of the labels specific to this metric. + Labels []LabelDescriptor `json:"labels,omitempty"` + + // Type and value of metric data. + Type string `json:"type,omitempty"` + + // The type of value returned as part of this metric. + ValueType string `json:"value_type,omitempty"` + + // The units of the value returned as part of this metric. + Units string `json:"units,omitempty"` +} + +type LabelDescriptor struct { + // Key to use for the label. + Key string `json:"key,omitempty"` + + // Description of the label. + Description string `json:"description,omitempty"` +} diff --git a/vendor/k8s.io/heapster/metrics/apis/metrics/handlers.go b/vendor/k8s.io/heapster/metrics/apis/metrics/handlers.go new file mode 100644 index 0000000000..443c4c9b5a --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/apis/metrics/handlers.go @@ -0,0 +1,293 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The file path is compatible with Kubernetes standards. This not a requirement +// right now but in the future we want to reuse apiserver code, which +// requires it. + +package metrics + +import ( + "fmt" + "net/http" + "time" + + restful "github.com/emicklei/go-restful" + "github.com/golang/glog" + + "k8s.io/heapster/metrics/apis/metrics/v1alpha1" + "k8s.io/heapster/metrics/core" + metricsink "k8s.io/heapster/metrics/sinks/metric" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/resource" + kube_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + kube_v1 "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/client/cache" + "k8s.io/kubernetes/pkg/labels" +) + +type Api struct { + metricSink *metricsink.MetricSink + podLister *cache.StoreToPodLister + nodeLister *cache.StoreToNodeLister +} + +func NewApi(metricSink *metricsink.MetricSink, podLister *cache.StoreToPodLister, nodeLister *cache.StoreToNodeLister) *Api { + return &Api{ + metricSink: metricSink, + podLister: podLister, + nodeLister: nodeLister, + } +} + +func (a *Api) Register(container *restful.Container) { + ws := new(restful.WebService) + ws.Path("/apis/metrics/v1alpha1"). + Doc("Root endpoint of metrics API"). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/nodes/"). + To(a.nodeMetricsList). + Doc("Get a list of metrics for all available nodes."). + Operation("nodeMetricsList")). + Param(ws.QueryParameter("labelSelector", "A selector to restrict the list of returned objects by their labels. Defaults to everything.").DataType("string")) + + ws.Route(ws.GET("/nodes/{node-name}/"). + To(a.nodeMetrics). + Doc("Get a list of all available metrics for the specified node."). + Operation("nodeMetrics"). + Param(ws.PathParameter("node-name", "The name of the node to lookup").DataType("string"))) + + ws.Route(ws.GET("/pods/"). + To(a.allPodMetricsList). + Doc("Get metrics for all available pods."). + Operation("allPodMetricsList")) + + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/"). + To(a.podMetricsList). + Doc("Get a list of metrics for all available pods in the specified namespace."). + Operation("podMetricsList"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string"))). + Param(ws.QueryParameter("labelSelector", "A selector to restrict the list of returned objects by their labels. Defaults to everything.").DataType("string")) + + ws.Route(ws.GET("/namespaces/{namespace-name}/pods/{pod-name}/"). + To(a.podMetrics). + Doc("Get metrics for the specified pod in the specified namespace."). + Operation("podMetrics"). + Param(ws.PathParameter("namespace-name", "The name of the namespace to lookup").DataType("string")). + Param(ws.PathParameter("pod-name", "The name of the pod to lookup").DataType("string"))) + + container.Add(ws) +} + +func (a *Api) nodeMetricsList(request *restful.Request, response *restful.Response) { + selector := request.QueryParameter("labelSelector") + + labelSelector, err := labels.Parse(selector) + if err != nil { + errMsg := fmt.Errorf("Error while parsing selector %v: %v", selector, err) + glog.Error(errMsg) + response.WriteError(http.StatusBadRequest, errMsg) + return + } + + nodes, err := a.nodeLister.NodeCondition(func(node *kube_api.Node) bool { + if labelSelector.Empty() { + return true + } + return labelSelector.Matches(labels.Set(node.Labels)) + }).List() + if err != nil { + errMsg := fmt.Errorf("Error while listing nodes: %v", err) + glog.Error(errMsg) + response.WriteError(http.StatusInternalServerError, errMsg) + return + } + + res := v1alpha1.NodeMetricsList{} + for _, node := range nodes { + if m := a.getNodeMetrics(node.Name); m != nil { + res.Items = append(res.Items, *m) + } + } + response.WriteEntity(&res) +} + +func (a *Api) nodeMetrics(request *restful.Request, response *restful.Response) { + node := request.PathParameter("node-name") + m := a.getNodeMetrics(node) + if m == nil { + response.WriteError(http.StatusNotFound, fmt.Errorf("No metrics for ode %v", node)) + return + } + response.WriteEntity(m) +} + +func (a *Api) getNodeMetrics(node string) *v1alpha1.NodeMetrics { + batch := a.metricSink.GetLatestDataBatch() + if batch == nil { + return nil + } + + ms, found := batch.MetricSets[core.NodeKey(node)] + if !found { + return nil + } + + usage, err := parseResourceList(ms) + if err != nil { + return nil + } + + return &v1alpha1.NodeMetrics{ + ObjectMeta: kube_v1.ObjectMeta{ + Name: node, + CreationTimestamp: kube_unversioned.NewTime(time.Now()), + }, + Timestamp: kube_unversioned.NewTime(batch.Timestamp), + Window: kube_unversioned.Duration{Duration: time.Minute}, + Usage: usage, + } +} + +func parseResourceList(ms *core.MetricSet) (kube_v1.ResourceList, error) { + cpu, found := ms.MetricValues[core.MetricCpuUsageRate.MetricDescriptor.Name] + if !found { + return kube_v1.ResourceList{}, fmt.Errorf("cpu not found") + } + mem, found := ms.MetricValues[core.MetricMemoryWorkingSet.MetricDescriptor.Name] + if !found { + return kube_v1.ResourceList{}, fmt.Errorf("memory not found") + } + + return kube_v1.ResourceList{ + kube_v1.ResourceCPU: *resource.NewMilliQuantity( + cpu.IntValue, + resource.DecimalSI), + kube_v1.ResourceMemory: *resource.NewQuantity( + mem.IntValue, + resource.BinarySI), + }, nil +} + +func (a *Api) allPodMetricsList(request *restful.Request, response *restful.Response) { + podMetricsInNamespaceList(a, request, response, kube_api.NamespaceAll) +} + +func (a *Api) podMetricsList(request *restful.Request, response *restful.Response) { + podMetricsInNamespaceList(a, request, response, request.PathParameter("namespace-name")) +} + +func podMetricsInNamespaceList(a *Api, request *restful.Request, response *restful.Response, namespace string) { + selector := request.QueryParameter("labelSelector") + + labelSelector, err := labels.Parse(selector) + if err != nil { + errMsg := fmt.Errorf("Error while parsing selector %v: %v", selector, err) + glog.Error(errMsg) + response.WriteError(http.StatusBadRequest, errMsg) + return + } + + pods, err := a.podLister.Pods(namespace).List(labelSelector) + if err != nil { + errMsg := fmt.Errorf("Error while listing pods for selector %v: %v", selector, err) + glog.Error(errMsg) + response.WriteError(http.StatusInternalServerError, errMsg) + return + } + + res := v1alpha1.PodMetricsList{} + for _, pod := range pods.Items { + if m := a.getPodMetrics(&pod); m != nil { + res.Items = append(res.Items, *m) + } else { + glog.Infof("No metrics for pod %s/%s", pod.Namespace, pod.Name) + } + } + response.WriteEntity(&res) +} + +func (a *Api) podMetrics(request *restful.Request, response *restful.Response) { + ns := request.PathParameter("namespace-name") + name := request.PathParameter("pod-name") + + o, exists, err := a.podLister.Get( + &kube_api.Pod{ + ObjectMeta: kube_api.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + ) + if err != nil { + errMsg := fmt.Errorf("Error while getting pod %v: %v", name, err) + glog.Error(errMsg) + response.WriteError(http.StatusInternalServerError, errMsg) + return + } + if !exists || o == nil { + response.WriteError(http.StatusNotFound, fmt.Errorf("Pod %v/%v not defined", ns, name)) + return + } + + pod, ok := o.(*kube_api.Pod) + if !ok { + errMsg := fmt.Errorf("Error while converting pod %v: %v", name, err) + glog.Error(errMsg) + response.WriteError(http.StatusInternalServerError, errMsg) + return + } + + if m := a.getPodMetrics(pod); m != nil { + response.WriteEntity(m) + } else { + response.WriteError(http.StatusNotFound, fmt.Errorf("No metrics availalble for pod %v/%v", ns, name)) + } +} + +func (a *Api) getPodMetrics(pod *kube_api.Pod) *v1alpha1.PodMetrics { + batch := a.metricSink.GetLatestDataBatch() + if batch == nil { + return nil + } + + res := &v1alpha1.PodMetrics{ + ObjectMeta: kube_v1.ObjectMeta{ + Name: pod.Name, + Namespace: pod.Namespace, + CreationTimestamp: kube_unversioned.NewTime(time.Now()), + }, + Timestamp: kube_unversioned.NewTime(batch.Timestamp), + Window: kube_unversioned.Duration{Duration: time.Minute}, + Containers: make([]v1alpha1.ContainerMetrics, 0), + } + + for _, c := range pod.Spec.Containers { + ms, found := batch.MetricSets[core.PodContainerKey(pod.Namespace, pod.Name, c.Name)] + if !found { + glog.Infof("No metrics for container %s in pod %s/%s", c.Name, pod.Namespace, pod.Name) + return nil + } + + usage, err := parseResourceList(ms) + if err != nil { + return nil + } + + res.Containers = append(res.Containers, v1alpha1.ContainerMetrics{Name: c.Name, Usage: usage}) + } + + return res +} diff --git a/vendor/k8s.io/heapster/metrics/apis/metrics/v1alpha1/types.go b/vendor/k8s.io/heapster/metrics/apis/metrics/v1alpha1/types.go new file mode 100644 index 0000000000..430a01bd11 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/apis/metrics/v1alpha1/types.go @@ -0,0 +1,78 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1alpha1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" +) + +// resource usage metrics of a node. +type NodeMetrics struct { + unversioned.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + + // The following fields define time interval from which metrics were + // collected from the interval [Timestamp-Window, Timestamp]. + Timestamp unversioned.Time `json:"timestamp"` + Window unversioned.Duration `json:"window"` + + // The memory usage is the memory working set. + Usage v1.ResourceList `json:"usage"` +} + +// NodeMetricsList is a list of NodeMetrics. +type NodeMetricsList struct { + unversioned.TypeMeta `json:",inline"` + // Standard list metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + unversioned.ListMeta `json:"metadata,omitempty"` + + // List of node metrics. + Items []NodeMetrics `json:"items"` +} + +// resource usage metrics of a pod. +type PodMetrics struct { + unversioned.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` + + // The following fields define time interval from which metrics were + // collected from the interval [Timestamp-Window, Timestamp]. + Timestamp unversioned.Time `json:"timestamp"` + Window unversioned.Duration `json:"window"` + + // Metrics for all containers are collected within the same time window. + Containers []ContainerMetrics `json:"containers"` +} + +// PodMetricsList is a list of PodMetrics. +type PodMetricsList struct { + unversioned.TypeMeta `json:",inline"` + // Standard list metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds + unversioned.ListMeta `json:"metadata,omitempty"` + + // List of pod metrics. + Items []PodMetrics `json:"items"` +} + +// resource usage metrics of a container. +type ContainerMetrics struct { + // Container name corresponding to the one from pod.spec.containers. + Name string `json:"name"` + // The memory usage is the memory working set. + Usage v1.ResourceList `json:"usage"` +} diff --git a/vendor/k8s.io/heapster/metrics/auth.go b/vendor/k8s.io/heapster/metrics/auth.go new file mode 100644 index 0000000000..3671c744eb --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/auth.go @@ -0,0 +1,115 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "crypto/x509" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "k8s.io/kubernetes/pkg/auth/authenticator" + "k8s.io/kubernetes/pkg/auth/user" + x509request "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509" +) + +func newAuthHandler(handler http.Handler) (http.Handler, error) { + // Authn/Authz setup + authn, err := newAuthenticatorFromClientCAFile(*argTLSClientCAFile) + if err != nil { + return nil, err + } + + authz, err := newAuthorizerFromUserList(strings.Split(*argAllowedUsers, ",")...) + if err != nil { + return nil, err + } + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // Check authn + user, ok, err := authn.AuthenticateRequest(req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if !ok { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Check authz + allowed, err := authz.AuthorizeRequest(req, user) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if !allowed { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + + handler.ServeHTTP(w, req) + }), nil +} + +// newAuthenticatorFromClientCAFile returns an authenticator.Request or an error +func newAuthenticatorFromClientCAFile(clientCAFile string) (authenticator.Request, error) { + opts := x509request.DefaultVerifyOptions() + + // If at custom CA bundle is provided, load it (otherwise just use system roots) + if len(clientCAFile) > 0 { + if caData, err := ioutil.ReadFile(clientCAFile); err != nil { + return nil, err + } else if len(caData) > 0 { + roots := x509.NewCertPool() + if !roots.AppendCertsFromPEM(caData) { + return nil, fmt.Errorf("no valid certs found in %s", clientCAFile) + } + opts.Roots = roots + } + } + + return x509request.New(opts, x509request.CommonNameUserConversion), nil +} + +type Authorizer interface { + AuthorizeRequest(req *http.Request, user user.Info) (bool, error) +} + +func newAuthorizerFromUserList(allowedUsers ...string) (Authorizer, error) { + if len(allowedUsers) == 1 && len(allowedUsers[0]) == 0 { + return &allowAnyAuthorizer{}, nil + } + u := map[string]bool{} + for _, allowedUser := range allowedUsers { + u[allowedUser] = true + } + return &userAuthorizer{u}, nil +} + +type allowAnyAuthorizer struct{} + +func (a *allowAnyAuthorizer) AuthorizeRequest(req *http.Request, user user.Info) (bool, error) { + return true, nil +} + +type userAuthorizer struct { + allowedUsers map[string]bool +} + +func (a *userAuthorizer) AuthorizeRequest(req *http.Request, user user.Info) (bool, error) { + return a.allowedUsers[user.GetName()], nil +} diff --git a/vendor/k8s.io/heapster/metrics/core/historical_types.go b/vendor/k8s.io/heapster/metrics/core/historical_types.go new file mode 100644 index 0000000000..cb8f1dc467 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/core/historical_types.go @@ -0,0 +1,171 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package core + +import ( + "fmt" + "time" +) + +type AggregationType string + +var ( + AggregationTypeAverage AggregationType = "average" + AggregationTypeMaximum AggregationType = "max" + AggregationTypeMinimum AggregationType = "min" + AggregationTypeMedian AggregationType = "median" + AggregationTypeCount AggregationType = "count" + AggregationTypePercentile50 AggregationType = "50-perc" + AggregationTypePercentile95 AggregationType = "95-perc" + AggregationTypePercentile99 AggregationType = "99-perc" +) + +// MultiTypedAggregations is the list of aggregations that can be either float or int +var MultiTypedAggregations = []AggregationType{ + AggregationTypeAverage, + AggregationTypeMaximum, + AggregationTypeMinimum, + AggregationTypeMedian, + AggregationTypePercentile50, + AggregationTypePercentile95, + AggregationTypePercentile99, +} + +// AllAggregations is the set of all supported aggregations +var AllAggregations = map[AggregationType]bool{ + AggregationTypeAverage: true, + AggregationTypeMaximum: true, + AggregationTypeMinimum: true, + AggregationTypeMedian: true, + AggregationTypePercentile50: true, + AggregationTypePercentile95: true, + AggregationTypePercentile99: true, + AggregationTypeCount: true, +} + +// TimestampedMetricValue is a metric value with an associated timestamp +type TimestampedMetricValue struct { + MetricValue + Timestamp time.Time +} + +// AggregationValue is a description of aggregated MetricValues over time +type AggregationValue struct { + Count *uint64 + + Aggregations map[AggregationType]MetricValue +} + +// TimestampedAggregationValue is an aggregation value with an associated timestamp +// and bucket size +type TimestampedAggregationValue struct { + // Timestamp is the start time of the bucket + Timestamp time.Time + + // BucketSize is the duration of the bucket + BucketSize time.Duration + + AggregationValue +} + +// HistoricalKey is an identifier pointing to a particular object. +// Is is composed of an object type (pod, namespace, container, etc) as well +// as a series of fields which identify that object. +type HistoricalKey struct { + // ObjectType specifies which type of object this is for (pod, namespace, etc) + // It should be one of the MetricSetType* labels. + ObjectType string + + // NodeName is used for node and system-container metrics + NodeName string + // NamespaceName is used for namespace, pod, and pod-container metrics + NamespaceName string + // PodName is used for pod and pod-container metrics + PodName string + // ContainerName is used for system-container and pod-container metrics + ContainerName string + // PodId may be used in place of the combination of PodName and NamespaceName for pod and pod-container metrics + PodId string +} + +func (key *HistoricalKey) String() string { + prefix := fmt.Sprintf("(%s)", key.ObjectType) + + var path string = "[unknown type]" + switch key.ObjectType { + case MetricSetTypeSystemContainer: + path = fmt.Sprintf("node:%s/container:%s", key.NodeName, key.ContainerName) + case MetricSetTypePodContainer: + if key.PodId != "" { + path = fmt.Sprintf("poduid:%s/container:%s", key.PodId, key.ContainerName) + } else { + path = fmt.Sprintf("ns:%s/pod:%s/container:%s", key.NamespaceName, key.PodName, key.ContainerName) + } + case MetricSetTypePod: + if key.PodId != "" { + path = fmt.Sprintf("poduid:%s", key.PodId) + } else { + path = fmt.Sprintf("ns:%s/pod:%s", key.NamespaceName, key.PodName) + } + case MetricSetTypeNamespace: + path = fmt.Sprintf("ns:%s", key.NamespaceName) + case MetricSetTypeNode: + path = fmt.Sprintf("node:%s", key.NodeName) + case MetricSetTypeCluster: + path = "[cluster]" + } + + return prefix + path +} + +// HistoricalSource allows for retrieval of historical metrics and aggregations from sinks +type HistoricalSource interface { + // GetMetric retrieves the given metric for one or more objects (specified by metricKeys) of + // the same type, within the given time interval. A start time of zero indicates no starting bound, + // while an end time of zero indicates no ending bound. + GetMetric(metricName string, metricKeys []HistoricalKey, start, end time.Time) (map[HistoricalKey][]TimestampedMetricValue, error) + + // GetLabeledMetric retrieves the given labeled metric. Otherwise, it functions identically to GetMetric. + GetLabeledMetric(metricName string, labels map[string]string, metricKeys []HistoricalKey, start, end time.Time) (map[HistoricalKey][]TimestampedMetricValue, error) + + // GetAggregation fetches the given aggregations for one or more objects (specified by metricKeys) of + // the same type, within the given time interval, calculated over a series of buckets. The start time, + // end time, and bucket size may be zero. A start time of zero indicates no starting bound, while and + // end time of zero indicates no ending bound (effectively meaning up to the latest metrics, but not metrics + // from the future). A bucket size of zero indicates that only a single bucket spanning the entire specified + // time range should be returned. + GetAggregation(metricName string, aggregations []AggregationType, metricKeys []HistoricalKey, start, end time.Time, bucketSize time.Duration) (map[HistoricalKey][]TimestampedAggregationValue, error) + + // GetLabeledAggregation fetches a the given aggregations for a labeled metric instead of a normal metric. + // Otherwise, it functions identically to GetAggregation. + GetLabeledAggregation(metricName string, labels map[string]string, aggregations []AggregationType, metricKeys []HistoricalKey, start, end time.Time, bucketSize time.Duration) (map[HistoricalKey][]TimestampedAggregationValue, error) + + // GetMetricNames retrieves the available metric names for the given object + GetMetricNames(metricKey HistoricalKey) ([]string, error) + + // GetNodes retrieves the list of nodes in the cluster + GetNodes() ([]string, error) + // GetNamespaces retrieves the list of namespaces in the cluster + GetNamespaces() ([]string, error) + // GetPodsFromNamespace retrieves the list of pods in a given namespace + GetPodsFromNamespace(namespace string) ([]string, error) + // GetSystemContainersFromNode retrieves the list of free containers for a given node + GetSystemContainersFromNode(node string) ([]string, error) +} + +// AsHistoricalSource represents sinks which support a historical access interface +type AsHistoricalSource interface { + // Historical returns the historical data access interface for this sink + Historical() HistoricalSource +} diff --git a/vendor/k8s.io/heapster/metrics/core/labels.go b/vendor/k8s.io/heapster/metrics/core/labels.go new file mode 100644 index 0000000000..3afa396bbb --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/core/labels.go @@ -0,0 +1,193 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core + +// Definition of labels supported in MetricSet. + +var ( + LabelMetricSetType = LabelDescriptor{ + Key: "type", + Description: "Type of the metrics set (container, pod, namespace, node, cluster)", + } + MetricSetTypeSystemContainer = "sys_container" + MetricSetTypePodContainer = "pod_container" + MetricSetTypePod = "pod" + MetricSetTypeNamespace = "ns" + MetricSetTypeNode = "node" + MetricSetTypeCluster = "cluster" + + LabelPodId = LabelDescriptor{ + Key: "pod_id", + Description: "The unique ID of the pod", + } + LabelPodName = LabelDescriptor{ + Key: "pod_name", + Description: "The name of the pod", + } + // Deprecated label + LabelPodNamespace = LabelDescriptor{ + Key: "pod_namespace", + Description: "The namespace of the pod", + } + LabelNamespaceName = LabelDescriptor{ + Key: "namespace_name", + Description: "The name of the namespace", + } + LabelPodNamespaceUID = LabelDescriptor{ + Key: "namespace_id", + Description: "The UID of namespace of the pod", + } + LabelContainerName = LabelDescriptor{ + Key: "container_name", + Description: "User-provided name of the container or full container name for system containers", + } + LabelLabels = LabelDescriptor{ + Key: "labels", + Description: "Comma-separated list of user-provided labels", + } + LabelNodename = LabelDescriptor{ + Key: "nodename", + Description: "nodename where the container ran", + } + LabelHostname = LabelDescriptor{ + Key: "hostname", + Description: "Hostname where the container ran", + } + LabelResourceID = LabelDescriptor{ + Key: "resource_id", + Description: "Identifier(s) specific to a metric", + } + LabelHostID = LabelDescriptor{ + Key: "host_id", + Description: "Identifier specific to a host. Set by cloud provider or user", + } + LabelContainerBaseImage = LabelDescriptor{ + Key: "container_base_image", + Description: "User-defined image name that is run inside the container", + } + // The label is populated only for GCM + LabelCustomMetricName = LabelDescriptor{ + Key: "custom_metric_name", + Description: "User-defined name of the exported custom metric", + } + LabelGCEResourceID = LabelDescriptor{ + Key: "compute.googleapis.com/resource_id", + Description: "Resource id for nodes specific for GCE.", + } + LabelGCEResourceType = LabelDescriptor{ + Key: "compute.googleapis.com/resource_type", + Description: "Resource types for nodes specific for GCE.", + } +) + +type LabelDescriptor struct { + // Key to use for the label. + Key string `json:"key,omitempty"` + + // Description of the label. + Description string `json:"description,omitempty"` +} + +var commonLabels = []LabelDescriptor{ + LabelNodename, + LabelHostname, + LabelHostID, +} + +var containerLabels = []LabelDescriptor{ + LabelContainerName, + LabelContainerBaseImage, +} + +var podLabels = []LabelDescriptor{ + LabelPodName, + LabelPodId, + LabelPodNamespace, + LabelPodNamespaceUID, + LabelLabels, +} + +var metricLabels = []LabelDescriptor{ + LabelResourceID, +} + +var customMetricLabels = []LabelDescriptor{ + LabelCustomMetricName, +} + +// Labels exported to GCM. The number of labels that can be exported to GCM is limited by 10. +var gcmLabels = []LabelDescriptor{ + LabelMetricSetType, + LabelPodName, + LabelNamespaceName, + LabelHostname, + LabelHostID, + LabelContainerName, + LabelContainerBaseImage, + LabelCustomMetricName, + LabelResourceID, +} + +var gcmNodeAutoscalingLabels = []LabelDescriptor{ + LabelGCEResourceID, + LabelGCEResourceType, + LabelHostname, +} + +func CommonLabels() []LabelDescriptor { + result := make([]LabelDescriptor, len(commonLabels)) + copy(result, commonLabels) + return result +} + +func ContainerLabels() []LabelDescriptor { + result := make([]LabelDescriptor, len(containerLabels)) + copy(result, containerLabels) + return result +} + +func PodLabels() []LabelDescriptor { + result := make([]LabelDescriptor, len(podLabels)) + copy(result, podLabels) + return result +} + +func MetricLabels() []LabelDescriptor { + result := make([]LabelDescriptor, len(metricLabels)+len(customMetricLabels)) + copy(result, metricLabels) + copy(result, customMetricLabels) + return result +} + +func SupportedLabels() []LabelDescriptor { + result := CommonLabels() + result = append(result, PodLabels()...) + return append(result, MetricLabels()...) +} + +func GcmLabels() map[string]LabelDescriptor { + result := make(map[string]LabelDescriptor, len(gcmLabels)) + for _, l := range gcmLabels { + result[l.Key] = l + } + return result +} +func GcmNodeAutoscalingLabels() map[string]LabelDescriptor { + result := make(map[string]LabelDescriptor, len(gcmNodeAutoscalingLabels)) + for _, l := range gcmNodeAutoscalingLabels { + result[l.Key] = l + } + return result +} diff --git a/vendor/k8s.io/heapster/metrics/core/metrics.go b/vendor/k8s.io/heapster/metrics/core/metrics.go new file mode 100644 index 0000000000..e72a64d600 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/core/metrics.go @@ -0,0 +1,562 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core + +import ( + "time" + + cadvisor "github.com/google/cadvisor/info/v1" +) + +const ( + CustomMetricPrefix = "custom/" +) + +// Provided by Kubelet/cadvisor. +var StandardMetrics = []Metric{ + MetricUptime, + MetricCpuUsage, + MetricMemoryUsage, + MetricMemoryWorkingSet, + MetricMemoryPageFaults, + MetricMemoryMajorPageFaults, + MetricNetworkRx, + MetricNetworkRxErrors, + MetricNetworkTx, + MetricNetworkTxErrors} + +// Metrics computed based on cluster state using Kubernetes API. +var AdditionalMetrics = []Metric{ + MetricCpuRequest, + MetricCpuLimit, + MetricMemoryRequest, + MetricMemoryLimit} + +// Computed based on corresponding StandardMetrics. +var RateMetrics = []Metric{ + MetricCpuUsageRate, + MetricMemoryPageFaultsRate, + MetricMemoryMajorPageFaultsRate, + MetricNetworkRxRate, + MetricNetworkRxErrorsRate, + MetricNetworkTxRate, + MetricNetworkTxErrorsRate} + +var RateMetricsMapping = map[string]Metric{ + MetricCpuUsage.MetricDescriptor.Name: MetricCpuUsageRate, + MetricMemoryPageFaults.MetricDescriptor.Name: MetricMemoryPageFaultsRate, + MetricMemoryMajorPageFaults.MetricDescriptor.Name: MetricMemoryMajorPageFaultsRate, + MetricNetworkRx.MetricDescriptor.Name: MetricNetworkRxRate, + MetricNetworkRxErrors.MetricDescriptor.Name: MetricNetworkRxErrorsRate, + MetricNetworkTx.MetricDescriptor.Name: MetricNetworkTxRate, + MetricNetworkTxErrors.MetricDescriptor.Name: MetricNetworkTxErrorsRate} + +var LabeledMetrics = []Metric{ + MetricFilesystemUsage, + MetricFilesystemLimit, + MetricFilesystemAvailable, +} + +var NodeAutoscalingMetrics = []Metric{ + MetricNodeCpuCapacity, + MetricNodeMemoryCapacity, + MetricNodeCpuUtilization, + MetricNodeMemoryUtilization, + MetricNodeCpuReservation, + MetricNodeMemoryReservation, +} + +var AllMetrics = append(append(append(append(StandardMetrics, AdditionalMetrics...), RateMetrics...), LabeledMetrics...), + NodeAutoscalingMetrics...) + +// Definition of Standard Metrics. +var MetricUptime = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "uptime", + Description: "Number of milliseconds since the container was started", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsMilliseconds, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return !spec.CreationTime.IsZero() + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: time.Since(spec.CreationTime).Nanoseconds() / time.Millisecond.Nanoseconds()} + }, +} + +var MetricCpuUsage = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/usage", + Description: "Cumulative CPU usage on all cores", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsNanoseconds, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasCpu + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Cpu.Usage.Total)} + }, +} + +var MetricMemoryUsage = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/usage", + Description: "Total memory usage", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasMemory + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricGauge, + IntValue: int64(stat.Memory.Usage)} + }, +} + +var MetricMemoryWorkingSet = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/working_set", + Description: "Total working set usage. Working set is the memory being used and not easily dropped by the kernel", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasMemory + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricGauge, + IntValue: int64(stat.Memory.WorkingSet)} + }, +} + +var MetricMemoryPageFaults = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/page_faults", + Description: "Number of page faults", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsCount, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasMemory + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Memory.ContainerData.Pgfault)} + }, +} + +var MetricMemoryMajorPageFaults = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/major_page_faults", + Description: "Number of major page faults", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsCount, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasMemory + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Memory.ContainerData.Pgmajfault)} + }, +} + +var MetricNetworkRx = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/rx", + Description: "Cumulative number of bytes received over the network", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsBytes, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasNetwork + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Network.RxBytes)} + }, +} + +var MetricNetworkRxErrors = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/rx_errors", + Description: "Cumulative number of errors while receiving over the network", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsCount, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasNetwork + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Network.RxErrors)} + }, +} + +var MetricNetworkTx = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/tx", + Description: "Cumulative number of bytes sent over the network", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsBytes, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasNetwork + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Network.TxBytes)} + }, +} + +var MetricNetworkTxErrors = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/tx_errors", + Description: "Cumulative number of errors while sending over the network", + Type: MetricCumulative, + ValueType: ValueInt64, + Units: UnitsCount, + }, + HasValue: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasNetwork + }, + GetValue: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) MetricValue { + return MetricValue{ + ValueType: ValueInt64, + MetricType: MetricCumulative, + IntValue: int64(stat.Network.TxErrors)} + }, +} + +// Definition of Additional Metrics. +var MetricCpuRequest = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/request", + Description: "CPU request (the guaranteed amount of resources) in millicores. This metric is Kubernetes specific.", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsCount, + }, +} + +var MetricCpuLimit = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/limit", + Description: "CPU hard limit in millicores.", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsCount, + }, +} + +var MetricMemoryRequest = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/request", + Description: "Memory request (the guaranteed amount of resources) in bytes. This metric is Kubernetes specific.", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + }, +} + +var MetricMemoryLimit = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/limit", + Description: "Memory hard limit in bytes.", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + }, +} + +// Definition of Rate Metrics. +var MetricCpuUsageRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/usage_rate", + Description: "CPU usage on all cores in millicores", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsCount, + }, +} + +var MetricMemoryPageFaultsRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/page_faults_rate", + Description: "Rate of page faults in counts per second", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricMemoryMajorPageFaultsRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/major_page_faults_rate", + Description: "Rate of major page faults in counts per second", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNetworkRxRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/rx_rate", + Description: "Rate of bytes received over the network in bytes per second", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNetworkRxErrorsRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/rx_errors_rate", + Description: "Rate of errors sending over the network in errors per second", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNetworkTxRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/tx_rate", + Description: "Rate of bytes transmitted over the network in bytes per second", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNetworkTxErrorsRate = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "network/tx_errors_rate", + Description: "Rate of errors transmitting over the network in errors per second", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNodeCpuCapacity = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/node_capacity", + Description: "Cpu capacity of a node", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNodeMemoryCapacity = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/node_capacity", + Description: "Memory capacity of a node", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNodeCpuUtilization = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/node_utilization", + Description: "Cpu utilization as a share of node capacity", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNodeMemoryUtilization = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/node_utilization", + Description: "Memory utilization as a share of memory capacity", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNodeCpuReservation = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "cpu/node_reservation", + Description: "Share of cpu that is reserved on the node", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +var MetricNodeMemoryReservation = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "memory/node_reservation", + Description: "Share of memory that is reserved on the node", + Type: MetricGauge, + ValueType: ValueFloat, + Units: UnitsCount, + }, +} + +// Labeled metrics + +var MetricFilesystemUsage = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "filesystem/usage", + Description: "Total number of bytes consumed on a filesystem", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + Labels: metricLabels, + }, + HasLabeledMetric: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasFilesystem + }, + GetLabeledMetric: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) []LabeledMetric { + result := make([]LabeledMetric, 0, len(stat.Filesystem)) + for _, fs := range stat.Filesystem { + result = append(result, LabeledMetric{ + Name: "filesystem/usage", + Labels: map[string]string{ + LabelResourceID.Key: fs.Device, + }, + MetricValue: MetricValue{ + ValueType: ValueInt64, + MetricType: MetricGauge, + IntValue: int64(fs.Usage), + }, + }) + } + return result + }, +} + +var MetricFilesystemLimit = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "filesystem/limit", + Description: "The total size of filesystem in bytes", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + Labels: metricLabels, + }, + HasLabeledMetric: func(spec *cadvisor.ContainerSpec) bool { + return spec.HasFilesystem + }, + GetLabeledMetric: func(spec *cadvisor.ContainerSpec, stat *cadvisor.ContainerStats) []LabeledMetric { + result := make([]LabeledMetric, 0, len(stat.Filesystem)) + for _, fs := range stat.Filesystem { + result = append(result, LabeledMetric{ + Name: "filesystem/limit", + Labels: map[string]string{ + LabelResourceID.Key: fs.Device, + }, + MetricValue: MetricValue{ + ValueType: ValueInt64, + MetricType: MetricGauge, + IntValue: int64(fs.Limit), + }, + }) + } + return result + }, +} + +var MetricFilesystemAvailable = Metric{ + MetricDescriptor: MetricDescriptor{ + Name: "filesystem/available", + Description: "The number of available bytes remaining in a the filesystem", + Type: MetricGauge, + ValueType: ValueInt64, + Units: UnitsBytes, + Labels: metricLabels, + }, +} + +func IsNodeAutoscalingMetric(name string) bool { + for _, autoscalingMetric := range NodeAutoscalingMetrics { + if autoscalingMetric.MetricDescriptor.Name == name { + return true + } + } + return false +} + +type MetricDescriptor struct { + // The unique name of the metric. + Name string `json:"name,omitempty"` + + // Description of the metric. + Description string `json:"description,omitempty"` + + // Descriptor of the labels specific to this metric. + Labels []LabelDescriptor `json:"labels,omitempty"` + + // Type and value of metric data. + Type MetricType `json:"type,omitempty"` + ValueType ValueType `json:"value_type,omitempty"` + Units UnitsType `json:"units,omitempty"` +} + +// Metric represents a resource usage stat metric. +type Metric struct { + MetricDescriptor + + // Returns whether this metric is present. + HasValue func(*cadvisor.ContainerSpec) bool + + // Returns a slice of internal point objects that contain metric values and associated labels. + GetValue func(*cadvisor.ContainerSpec, *cadvisor.ContainerStats) MetricValue + + // Returns whether this metric is present. + HasLabeledMetric func(*cadvisor.ContainerSpec) bool + + // Returns a slice of internal point objects that contain metric values and associated labels. + GetLabeledMetric func(*cadvisor.ContainerSpec, *cadvisor.ContainerStats) []LabeledMetric +} diff --git a/vendor/k8s.io/heapster/metrics/core/ms_keys.go b/vendor/k8s.io/heapster/metrics/core/ms_keys.go new file mode 100644 index 0000000000..7325ba012c --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/core/ms_keys.go @@ -0,0 +1,48 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core + +import ( + "fmt" +) + +// MetricsSet keys inside of DataBatch. The structure of the returned string is +// an implementation detail and no component should rely on it as it may change +// anytime. It it only guaranteed that it is unique for the unique combination of +// passed parameters. + +func PodContainerKey(namespace, podName, containerName string) string { + return fmt.Sprintf("namespace:%s/pod:%s/container:%s", namespace, podName, containerName) +} + +func PodKey(namespace, podName string) string { + return fmt.Sprintf("namespace:%s/pod:%s", namespace, podName) +} + +func NamespaceKey(namespace string) string { + return fmt.Sprintf("namespace:%s", namespace) +} + +func NodeKey(node string) string { + return fmt.Sprintf("node:%s", node) +} + +func NodeContainerKey(node, container string) string { + return fmt.Sprintf("node:%s/container:%s", node, container) +} + +func ClusterKey() string { + return "cluster" +} diff --git a/vendor/k8s.io/heapster/metrics/core/types.go b/vendor/k8s.io/heapster/metrics/core/types.go new file mode 100644 index 0000000000..727f6d537b --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/core/types.go @@ -0,0 +1,158 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package core + +import ( + "time" +) + +type MetricType int8 + +const ( + MetricCumulative MetricType = iota + MetricGauge + MetricDelta +) + +func (self *MetricType) String() string { + switch *self { + case MetricCumulative: + return "cumulative" + case MetricGauge: + return "gauge" + case MetricDelta: + return "delta" + } + return "" +} + +type ValueType int8 + +const ( + ValueInt64 ValueType = iota + ValueFloat +) + +func (self *ValueType) String() string { + switch *self { + case ValueInt64: + return "int64" + case ValueFloat: + return "double" + } + return "" +} + +type UnitsType int8 + +const ( + // A counter metric. + UnitsCount UnitsType = iota + // A metric in bytes. + UnitsBytes + // A metric in milliseconds. + UnitsMilliseconds + // A metric in nanoseconds. + UnitsNanoseconds + // A metric in millicores. + UnitsMillicores +) + +func (self *UnitsType) String() string { + switch *self { + case UnitsBytes: + return "bytes" + case UnitsMilliseconds: + return "ms" + case UnitsNanoseconds: + return "ns" + case UnitsMillicores: + return "millicores" + } + return "" +} + +type MetricValue struct { + IntValue int64 + FloatValue float32 + MetricType MetricType + ValueType ValueType +} + +func (this *MetricValue) GetValue() interface{} { + if ValueInt64 == this.ValueType { + return this.IntValue + } else if ValueFloat == this.ValueType { + return this.FloatValue + } else { + return nil + } +} + +type LabeledMetric struct { + Name string + Labels map[string]string + MetricValue +} + +func (this *LabeledMetric) GetValue() interface{} { + if ValueInt64 == this.ValueType { + return this.IntValue + } else if ValueFloat == this.ValueType { + return this.FloatValue + } else { + return nil + } +} + +type MetricSet struct { + CreateTime time.Time + ScrapeTime time.Time + MetricValues map[string]MetricValue + Labels map[string]string + LabeledMetrics []LabeledMetric +} + +type DataBatch struct { + Timestamp time.Time + // Should use key functions from ms_keys.go + MetricSets map[string]*MetricSet +} + +// A place from where the metrics should be scraped. +type MetricsSource interface { + Name() string + ScrapeMetrics(start, end time.Time) *DataBatch +} + +// Provider of list of sources to be scaped. +type MetricsSourceProvider interface { + GetMetricsSources() []MetricsSource +} + +type DataSink interface { + Name() string + + // Exports data to the external storge. The funciton should be synchronous/blocking and finish only + // after the given DataBatch was written. This will allow sink manager to push data only to these + // sinks that finished writing the previous data. + ExportData(*DataBatch) + Stop() +} + +type DataProcessor interface { + Name() string + Process(*DataBatch) (*DataBatch, error) +} diff --git a/vendor/k8s.io/heapster/metrics/handlers.go b/vendor/k8s.io/heapster/metrics/handlers.go new file mode 100644 index 0000000000..9e2e477064 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/handlers.go @@ -0,0 +1,68 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + "net/http/pprof" + "strings" + + restful "github.com/emicklei/go-restful" + "k8s.io/heapster/metrics/api/v1" + metricsApi "k8s.io/heapster/metrics/apis/metrics" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/sinks/metric" + "k8s.io/heapster/metrics/util/metrics" + + "k8s.io/kubernetes/pkg/client/cache" +) + +const pprofBasePath = "/debug/pprof/" + +func setupHandlers(metricSink *metricsink.MetricSink, podLister *cache.StoreToPodLister, nodeLister *cache.StoreToNodeLister, historicalSource core.HistoricalSource) http.Handler { + + runningInKubernetes := true + + // Make API handler. + wsContainer := restful.NewContainer() + wsContainer.EnableContentEncoding(true) + wsContainer.Router(restful.CurlyRouter{}) + a := v1.NewApi(runningInKubernetes, metricSink, historicalSource) + a.Register(wsContainer) + // Metrics API + m := metricsApi.NewApi(metricSink, podLister, nodeLister) + m.Register(wsContainer) + + handlePprofEndpoint := func(req *restful.Request, resp *restful.Response) { + name := strings.TrimPrefix(req.Request.URL.Path, pprofBasePath) + switch name { + case "profile": + pprof.Profile(resp, req.Request) + case "symbol": + pprof.Symbol(resp, req.Request) + case "cmdline": + pprof.Cmdline(resp, req.Request) + default: + pprof.Index(resp, req.Request) + } + } + + // Setup pporf handlers. + ws := new(restful.WebService).Path(pprofBasePath) + ws.Route(ws.GET("/{subpath:*}").To(metrics.InstrumentRouteFunc("pprof", handlePprofEndpoint))).Doc("pprof endpoint") + wsContainer.Add(ws) + + return wsContainer +} diff --git a/vendor/k8s.io/heapster/metrics/heapster.go b/vendor/k8s.io/heapster/metrics/heapster.go new file mode 100644 index 0000000000..9ddf5ed898 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/heapster.go @@ -0,0 +1,282 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:generate ./hooks/run_extpoints.sh + +package main + +import ( + "crypto/tls" + "flag" + "fmt" + "net/http" + "net/url" + "os" + "runtime" + "strings" + "time" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/heapster/common/flags" + kube_config "k8s.io/heapster/common/kubernetes" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/manager" + "k8s.io/heapster/metrics/processors" + "k8s.io/heapster/metrics/sinks" + "k8s.io/heapster/metrics/sources" + "k8s.io/heapster/version" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + kube_client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/fields" +) + +var ( + argMetricResolution = flag.Duration("metric_resolution", 60*time.Second, "The resolution at which heapster will retain metrics.") + argPort = flag.Int("port", 8082, "port to listen to") + argIp = flag.String("listen_ip", "", "IP to listen on, defaults to all IPs") + argMaxProcs = flag.Int("max_procs", 0, "max number of CPUs that can be used simultaneously. Less than 1 for default (number of cores)") + argTLSCertFile = flag.String("tls_cert", "", "file containing TLS certificate") + argTLSKeyFile = flag.String("tls_key", "", "file containing TLS key") + argTLSClientCAFile = flag.String("tls_client_ca", "", "file containing TLS client CA for client cert validation") + argAllowedUsers = flag.String("allowed_users", "", "comma-separated list of allowed users") + argSources flags.Uris + argSinks flags.Uris + argHistoricalSource = flag.String("historical_source", "", "which source type to use for the historical API (should be exactly the same as one of the sink URIs), or empty to disable the historical API") +) + +func main() { + defer glog.Flush() + flag.Var(&argSources, "source", "source(s) to watch") + flag.Var(&argSinks, "sink", "external sink(s) that receive data") + flag.Parse() + setMaxProcs() + glog.Infof(strings.Join(os.Args, " ")) + glog.Infof("Heapster version %v", version.HeapsterVersion) + if err := validateFlags(); err != nil { + glog.Fatal(err) + } + + // sources + if len(argSources) != 1 { + glog.Fatal("Wrong number of sources specified") + } + sourceFactory := sources.NewSourceFactory() + sourceProvider, err := sourceFactory.BuildAll(argSources) + if err != nil { + glog.Fatalf("Failed to create source provide: %v", err) + } + sourceManager, err := sources.NewSourceManager(sourceProvider, sources.DefaultMetricsScrapeTimeout) + if err != nil { + glog.Fatalf("Failed to create source manager: %v", err) + } + + // sinks + sinksFactory := sinks.NewSinkFactory() + metricSink, sinkList, historicalSource := sinksFactory.BuildAll(argSinks, *argHistoricalSource) + if metricSink == nil { + glog.Fatal("Failed to create metric sink") + } + if historicalSource == nil && len(*argHistoricalSource) > 0 { + glog.Fatal("Failed to use a sink as a historical metrics source") + } + for _, sink := range sinkList { + glog.Infof("Starting with %s", sink.Name()) + } + sinkManager, err := sinks.NewDataSinkManager(sinkList, sinks.DefaultSinkExportDataTimeout, sinks.DefaultSinkStopTimeout) + if err != nil { + glog.Fatalf("Failed to created sink manager: %v", err) + } + + // data processors + metricsToAggregate := []string{ + core.MetricCpuUsageRate.Name, + core.MetricMemoryUsage.Name, + core.MetricCpuRequest.Name, + core.MetricCpuLimit.Name, + core.MetricMemoryRequest.Name, + core.MetricMemoryLimit.Name, + } + + metricsToAggregateForNode := []string{ + core.MetricCpuRequest.Name, + core.MetricCpuLimit.Name, + core.MetricMemoryRequest.Name, + core.MetricMemoryLimit.Name, + } + + dataProcessors := []core.DataProcessor{ + // Convert cumulaties to rate + processors.NewRateCalculator(core.RateMetricsMapping), + } + + kubernetesUrl, err := getKubernetesAddress(argSources) + if err != nil { + glog.Fatalf("Failed to get kubernetes address: %v", err) + } + + kubeConfig, err := kube_config.GetKubeClientConfig(kubernetesUrl) + if err != nil { + glog.Fatalf("Failed to get client config: %v", err) + } + kubeClient := kube_client.NewOrDie(kubeConfig) + + podLister, err := getPodLister(kubeClient) + if err != nil { + glog.Fatalf("Failed to create podLister: %v", err) + } + nodeLister, err := getNodeLister(kubeClient) + if err != nil { + glog.Fatalf("Failed to create nodeLister: %v", err) + } + + podBasedEnricher, err := processors.NewPodBasedEnricher(podLister) + if err != nil { + glog.Fatalf("Failed to create PodBasedEnricher: %v", err) + } + dataProcessors = append(dataProcessors, podBasedEnricher) + + namespaceBasedEnricher, err := processors.NewNamespaceBasedEnricher(kubernetesUrl) + if err != nil { + glog.Fatalf("Failed to create NamespaceBasedEnricher: %v", err) + } + dataProcessors = append(dataProcessors, namespaceBasedEnricher) + + // then aggregators + dataProcessors = append(dataProcessors, + processors.NewPodAggregator(), + &processors.NamespaceAggregator{ + MetricsToAggregate: metricsToAggregate, + }, + &processors.NodeAggregator{ + MetricsToAggregate: metricsToAggregateForNode, + }, + &processors.ClusterAggregator{ + MetricsToAggregate: metricsToAggregate, + }) + + nodeAutoscalingEnricher, err := processors.NewNodeAutoscalingEnricher(kubernetesUrl) + if err != nil { + glog.Fatalf("Failed to create NodeAutoscalingEnricher: %v", err) + } + dataProcessors = append(dataProcessors, nodeAutoscalingEnricher) + + // main manager + manager, err := manager.NewManager(sourceManager, dataProcessors, sinkManager, *argMetricResolution, + manager.DefaultScrapeOffset, manager.DefaultMaxParallelism) + if err != nil { + glog.Fatalf("Failed to create main manager: %v", err) + } + manager.Start() + + handler := setupHandlers(metricSink, podLister, nodeLister, historicalSource) + addr := fmt.Sprintf("%s:%d", *argIp, *argPort) + glog.Infof("Starting heapster on port %d", *argPort) + + mux := http.NewServeMux() + promHandler := prometheus.Handler() + if len(*argTLSCertFile) > 0 && len(*argTLSKeyFile) > 0 { + if len(*argTLSClientCAFile) > 0 { + authPprofHandler, err := newAuthHandler(handler) + if err != nil { + glog.Fatalf("Failed to create authorized pprof handler: %v", err) + } + handler = authPprofHandler + + authPromHandler, err := newAuthHandler(promHandler) + if err != nil { + glog.Fatalf("Failed to create authorized prometheus handler: %v", err) + } + promHandler = authPromHandler + } + mux.Handle("/", handler) + mux.Handle("/metrics", promHandler) + + // If allowed users is set, then we need to enable Client Authentication + if len(*argAllowedUsers) > 0 { + server := &http.Server{ + Addr: addr, + Handler: mux, + TLSConfig: &tls.Config{ClientAuth: tls.RequestClientCert}, + } + glog.Fatal(server.ListenAndServeTLS(*argTLSCertFile, *argTLSKeyFile)) + } else { + glog.Fatal(http.ListenAndServeTLS(addr, *argTLSCertFile, *argTLSKeyFile, mux)) + } + + } else { + mux.Handle("/", handler) + mux.Handle("/metrics", promHandler) + glog.Fatal(http.ListenAndServe(addr, mux)) + } +} + +// Gets the address of the kubernetes source from the list of source URIs. +// Possible kubernetes sources are: 'kubernetes' and 'kubernetes.summary_api' +func getKubernetesAddress(args flags.Uris) (*url.URL, error) { + for _, uri := range args { + if strings.SplitN(uri.Key, ".", 2)[0] == "kubernetes" { + return &uri.Val, nil + } + } + return nil, fmt.Errorf("No kubernetes source found.") +} + +func getPodLister(kubeClient *kube_client.Client) (*cache.StoreToPodLister, error) { + lw := cache.NewListWatchFromClient(kubeClient, "pods", kube_api.NamespaceAll, fields.Everything()) + store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + podLister := &cache.StoreToPodLister{Indexer: store} + reflector := cache.NewReflector(lw, &kube_api.Pod{}, store, time.Hour) + reflector.Run() + return podLister, nil +} + +func getNodeLister(kubeClient *kube_client.Client) (*cache.StoreToNodeLister, error) { + lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything()) + nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)} + reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, time.Hour) + reflector.Run() + return nodeLister, nil +} + +func validateFlags() error { + if *argMetricResolution < 5*time.Second { + return fmt.Errorf("metric resolution needs to be greater than 5 seconds - %d", *argMetricResolution) + } + if (len(*argTLSCertFile) > 0 && len(*argTLSKeyFile) == 0) || (len(*argTLSCertFile) == 0 && len(*argTLSKeyFile) > 0) { + return fmt.Errorf("both TLS certificate & key are required to enable TLS serving") + } + if len(*argTLSClientCAFile) > 0 && len(*argTLSCertFile) == 0 { + return fmt.Errorf("client cert authentication requires TLS certificate & key") + } + return nil +} + +func setMaxProcs() { + // Allow as many threads as we have cores unless the user specified a value. + var numProcs int + if *argMaxProcs < 1 { + numProcs = runtime.NumCPU() + } else { + numProcs = *argMaxProcs + } + runtime.GOMAXPROCS(numProcs) + + // Check if the setting was successful. + actualNumProcs := runtime.GOMAXPROCS(0) + if actualNumProcs != numProcs { + glog.Warningf("Specified max procs of %d but using %d", numProcs, actualNumProcs) + } +} diff --git a/vendor/k8s.io/heapster/metrics/manager/manager.go b/vendor/k8s.io/heapster/metrics/manager/manager.go new file mode 100644 index 0000000000..e6a141e2b5 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/manager/manager.go @@ -0,0 +1,153 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package manager + +import ( + "time" + + "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + DefaultScrapeOffset = 5 * time.Second + DefaultMaxParallelism = 3 +) + +var ( + // The Time spent in a processor in microseconds. + processorDuration = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "heapster", + Subsystem: "processor", + Name: "duration_microseconds", + Help: "The Time spent in a processor in microseconds.", + }, + []string{"processor"}, + ) +) + +func init() { + prometheus.MustRegister(processorDuration) +} + +type Manager interface { + Start() + Stop() +} + +type realManager struct { + source core.MetricsSource + processors []core.DataProcessor + sink core.DataSink + resolution time.Duration + scrapeOffset time.Duration + stopChan chan struct{} + housekeepSemaphoreChan chan struct{} + housekeepTimeout time.Duration +} + +func NewManager(source core.MetricsSource, processors []core.DataProcessor, sink core.DataSink, resolution time.Duration, + scrapeOffset time.Duration, maxParallelism int) (Manager, error) { + manager := realManager{ + source: source, + processors: processors, + sink: sink, + resolution: resolution, + scrapeOffset: scrapeOffset, + stopChan: make(chan struct{}), + housekeepSemaphoreChan: make(chan struct{}, maxParallelism), + housekeepTimeout: resolution / 2, + } + + for i := 0; i < maxParallelism; i++ { + manager.housekeepSemaphoreChan <- struct{}{} + } + + return &manager, nil +} + +func (rm *realManager) Start() { + go rm.Housekeep() +} + +func (rm *realManager) Stop() { + rm.stopChan <- struct{}{} +} + +func (rm *realManager) Housekeep() { + for { + // Always try to get the newest metrics + now := time.Now() + start := now.Truncate(rm.resolution) + end := start.Add(rm.resolution) + timeToNextSync := end.Add(rm.scrapeOffset).Sub(now) + + select { + case <-time.After(timeToNextSync): + rm.housekeep(start, end) + case <-rm.stopChan: + rm.sink.Stop() + return + } + } +} + +func (rm *realManager) housekeep(start, end time.Time) { + if !start.Before(end) { + glog.Warningf("Wrong time provided to housekeep start:%s end: %s", start, end) + return + } + + select { + case <-rm.housekeepSemaphoreChan: + // ok, good to go + + case <-time.After(rm.housekeepTimeout): + glog.Warningf("Spent too long waiting for housekeeping to start") + return + } + + go func(rm *realManager) { + // should always give back the semaphore + defer func() { rm.housekeepSemaphoreChan <- struct{}{} }() + data := rm.source.ScrapeMetrics(start, end) + + for _, p := range rm.processors { + newData, err := process(p, data) + if err == nil { + data = newData + } else { + glog.Errorf("Error in processor: %v", err) + return + } + } + + // Export data to sinks + rm.sink.ExportData(data) + + }(rm) +} + +func process(p core.DataProcessor, data *core.DataBatch) (*core.DataBatch, error) { + startTime := time.Now() + defer processorDuration. + WithLabelValues(p.Name()). + Observe(float64(time.Since(startTime)) / float64(time.Microsecond)) + + return p.Process(data) +} diff --git a/vendor/k8s.io/heapster/metrics/manager/manager_test.go b/vendor/k8s.io/heapster/metrics/manager/manager_test.go new file mode 100644 index 0000000000..bcf3dc8f90 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/manager/manager_test.go @@ -0,0 +1,57 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package manager + +import ( + "testing" + "time" + + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/util" +) + +func TestFlow(t *testing.T) { + source := util.NewDummyMetricsSource("src", time.Millisecond) + sink := util.NewDummySink("sink", time.Millisecond) + processor := util.NewDummyDataProcessor(time.Millisecond) + + manager, _ := NewManager(source, []core.DataProcessor{processor}, sink, time.Second, time.Millisecond, 1) + manager.Start() + + // 4-5 cycles + time.Sleep(time.Millisecond * 4500) + manager.Stop() + + if sink.GetExportCount() < 4 || sink.GetExportCount() > 5 { + t.Fatalf("Wrong number of exports executed: %d", sink.GetExportCount()) + } +} + +func TestThrottling(t *testing.T) { + source := util.NewDummyMetricsSource("src", time.Millisecond) + sink := util.NewDummySink("sink", 4*time.Second) + processor := util.NewDummyDataProcessor(5 * time.Millisecond) + + manager, _ := NewManager(source, []core.DataProcessor{processor}, sink, time.Second, time.Millisecond, 1) + manager.Start() + + // 4-5 cycles + time.Sleep(time.Millisecond * 9500) + manager.Stop() + + if sink.GetExportCount() < 2 || sink.GetExportCount() > 3 { + t.Fatalf("Wrong number of exports executed: %d", sink.GetExportCount()) + } +} diff --git a/vendor/k8s.io/heapster/metrics/processors/cluster_aggregator.go b/vendor/k8s.io/heapster/metrics/processors/cluster_aggregator.go new file mode 100644 index 0000000000..e27fb2c4ee --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/cluster_aggregator.go @@ -0,0 +1,49 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import "k8s.io/heapster/metrics/core" + +type ClusterAggregator struct { + MetricsToAggregate []string +} + +func (this *ClusterAggregator) Name() string { + return "cluster_aggregator" +} + +func (this *ClusterAggregator) Process(batch *core.DataBatch) (*core.DataBatch, error) { + clusterKey := core.ClusterKey() + cluster := clusterMetricSet() + for _, metricSet := range batch.MetricSets { + if metricSetType, found := metricSet.Labels[core.LabelMetricSetType.Key]; found && + (metricSetType == core.MetricSetTypeNamespace || metricSetType == core.MetricSetTypeSystemContainer) { + if err := aggregate(metricSet, cluster, this.MetricsToAggregate); err != nil { + return nil, err + } + } + } + batch.MetricSets[clusterKey] = cluster + return batch, nil +} + +func clusterMetricSet() *core.MetricSet { + return &core.MetricSet{ + MetricValues: make(map[string]core.MetricValue), + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypeCluster, + }, + } +} diff --git a/vendor/k8s.io/heapster/metrics/processors/cluster_aggregator_test.go b/vendor/k8s.io/heapster/metrics/processors/cluster_aggregator_test.go new file mode 100644 index 0000000000..465b29c969 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/cluster_aggregator_test.go @@ -0,0 +1,84 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func TestClusterAggregate(t *testing.T) { + batch := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodKey("ns1", "pod1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypeNamespace, + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 10, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 222, + }, + }, + }, + + core.PodKey("ns1", "pod2"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypeNamespace, + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 100, + }, + "m3": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 30, + }, + }, + }, + }, + } + processor := ClusterAggregator{ + MetricsToAggregate: []string{"m1", "m3"}, + } + result, err := processor.Process(&batch) + assert.NoError(t, err) + cluster, found := result.MetricSets[core.ClusterKey()] + assert.True(t, found) + + m1, found := cluster.MetricValues["m1"] + assert.True(t, found) + assert.Equal(t, 110, m1.IntValue) + + m3, found := cluster.MetricValues["m3"] + assert.True(t, found) + assert.Equal(t, 30, m3.IntValue) +} diff --git a/vendor/k8s.io/heapster/metrics/processors/helper.go b/vendor/k8s.io/heapster/metrics/processors/helper.go new file mode 100644 index 0000000000..fefb47e9a0 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/helper.go @@ -0,0 +1,48 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "fmt" + + "k8s.io/heapster/metrics/core" +) + +func aggregate(src, dst *core.MetricSet, metricsToAggregate []string) error { + for _, metricName := range metricsToAggregate { + metricValue, found := src.MetricValues[metricName] + if !found { + continue + } + aggregatedValue, found := dst.MetricValues[metricName] + if found { + if aggregatedValue.ValueType != metricValue.ValueType { + return fmt.Errorf("NamespaceAggregator: type not supported in %s", metricName) + } + + if aggregatedValue.ValueType == core.ValueInt64 { + aggregatedValue.IntValue += metricValue.IntValue + } else if aggregatedValue.ValueType == core.ValueFloat { + aggregatedValue.FloatValue += metricValue.FloatValue + } else { + return fmt.Errorf("NamespaceAggregator: type not supported in %s", metricName) + } + } else { + aggregatedValue = metricValue + } + dst.MetricValues[metricName] = aggregatedValue + } + return nil +} diff --git a/vendor/k8s.io/heapster/metrics/processors/namespace_aggregator.go b/vendor/k8s.io/heapster/metrics/processors/namespace_aggregator.go new file mode 100644 index 0000000000..f61a835108 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/namespace_aggregator.go @@ -0,0 +1,69 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "github.com/golang/glog" + "k8s.io/heapster/metrics/core" +) + +type NamespaceAggregator struct { + MetricsToAggregate []string +} + +func (this *NamespaceAggregator) Name() string { + return "namespace_aggregator" +} + +func (this *NamespaceAggregator) Process(batch *core.DataBatch) (*core.DataBatch, error) { + namespaces := make(map[string]*core.MetricSet) + for key, metricSet := range batch.MetricSets { + if metricSetType, found := metricSet.Labels[core.LabelMetricSetType.Key]; found && metricSetType == core.MetricSetTypePod { + // Aggregating pods + if namespaceName, found := metricSet.Labels[core.LabelNamespaceName.Key]; found { + namespaceKey := core.NamespaceKey(namespaceName) + namespace, found := namespaces[namespaceKey] + if !found { + if nsFromBatch, found := batch.MetricSets[namespaceKey]; found { + namespace = nsFromBatch + } else { + namespace = namespaceMetricSet(namespaceName, metricSet.Labels[core.LabelPodNamespaceUID.Key]) + namespaces[namespaceKey] = namespace + } + } + if err := aggregate(metricSet, namespace, this.MetricsToAggregate); err != nil { + return nil, err + } + } else { + glog.Errorf("No namespace info in pod %s: %v", key, metricSet.Labels) + } + } + } + for key, val := range namespaces { + batch.MetricSets[key] = val + } + return batch, nil +} + +func namespaceMetricSet(namespaceName, uid string) *core.MetricSet { + return &core.MetricSet{ + MetricValues: make(map[string]core.MetricValue), + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypeNamespace, + core.LabelNamespaceName.Key: namespaceName, + core.LabelPodNamespaceUID.Key: uid, + }, + } +} diff --git a/vendor/k8s.io/heapster/metrics/processors/namespace_aggregator_test.go b/vendor/k8s.io/heapster/metrics/processors/namespace_aggregator_test.go new file mode 100644 index 0000000000..adc2edc056 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/namespace_aggregator_test.go @@ -0,0 +1,84 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func TestNamespaceAggregate(t *testing.T) { + batch := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodKey("ns1", "pod1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 10, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 222, + }, + }, + }, + + core.PodKey("ns1", "pod2"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 100, + }, + "m3": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 30, + }, + }, + }, + }, + } + processor := NamespaceAggregator{ + MetricsToAggregate: []string{"m1", "m3"}, + } + result, err := processor.Process(&batch) + assert.NoError(t, err) + namespace, found := result.MetricSets[core.NamespaceKey("ns1")] + assert.True(t, found) + + m1, found := namespace.MetricValues["m1"] + assert.True(t, found) + assert.Equal(t, 110, m1.IntValue) + + m3, found := namespace.MetricValues["m3"] + assert.True(t, found) + assert.Equal(t, 30, m3.IntValue) +} diff --git a/vendor/k8s.io/heapster/metrics/processors/namespace_based_enricher.go b/vendor/k8s.io/heapster/metrics/processors/namespace_based_enricher.go new file mode 100644 index 0000000000..158c93273b --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/namespace_based_enricher.go @@ -0,0 +1,91 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "net/url" + "time" + + "github.com/golang/glog" + + kube_config "k8s.io/heapster/common/kubernetes" + "k8s.io/heapster/metrics/core" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + kube_client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/fields" +) + +type NamespaceBasedEnricher struct { + store cache.Store + reflector *cache.Reflector +} + +func (this *NamespaceBasedEnricher) Name() string { + return "namespace_based_enricher" +} + +func (this *NamespaceBasedEnricher) Process(batch *core.DataBatch) (*core.DataBatch, error) { + for _, ms := range batch.MetricSets { + this.addNamespaceInfo(ms) + } + return batch, nil +} + +// Adds UID to all namespaced elements. +func (this *NamespaceBasedEnricher) addNamespaceInfo(metricSet *core.MetricSet) { + if metricSetType, found := metricSet.Labels[core.LabelMetricSetType.Key]; found && + (metricSetType == core.MetricSetTypePodContainer || + metricSetType == core.MetricSetTypePod || + metricSetType == core.MetricSetTypeNamespace) { + + if namespaceName, found := metricSet.Labels[core.LabelNamespaceName.Key]; found { + nsObj, exists, err := this.store.GetByKey(namespaceName) + if exists && err == nil { + namespace, ok := nsObj.(*kube_api.Namespace) + if ok { + metricSet.Labels[core.LabelPodNamespaceUID.Key] = string(namespace.UID) + } else { + glog.Errorf("Wrong namespace store content") + } + } else { + if err != nil { + glog.Warningf("Failed to get namespace %s: %v", namespaceName, err) + } else if !exists { + glog.Warningf("Namespace doesn't exist: %s", namespaceName) + } + } + } + } +} + +func NewNamespaceBasedEnricher(url *url.URL) (*NamespaceBasedEnricher, error) { + kubeConfig, err := kube_config.GetKubeClientConfig(url) + if err != nil { + return nil, err + } + kubeClient := kube_client.NewOrDie(kubeConfig) + + // watch nodes + lw := cache.NewListWatchFromClient(kubeClient, "namespaces", kube_api.NamespaceAll, fields.Everything()) + store := cache.NewStore(cache.MetaNamespaceKeyFunc) + reflector := cache.NewReflector(lw, &kube_api.Namespace{}, store, time.Hour) + reflector.Run() + + return &NamespaceBasedEnricher{ + store: store, + reflector: reflector, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/processors/node_aggregator.go b/vendor/k8s.io/heapster/metrics/processors/node_aggregator.go new file mode 100644 index 0000000000..be001a74f8 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/node_aggregator.go @@ -0,0 +1,54 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "github.com/golang/glog" + "k8s.io/heapster/metrics/core" +) + +// Does not add any nodes. +type NodeAggregator struct { + MetricsToAggregate []string +} + +func (this *NodeAggregator) Name() string { + return "node_aggregator" +} + +func (this *NodeAggregator) Process(batch *core.DataBatch) (*core.DataBatch, error) { + for key, metricSet := range batch.MetricSets { + if metricSetType, found := metricSet.Labels[core.LabelMetricSetType.Key]; found && metricSetType == core.MetricSetTypePod { + // Aggregating pods + nodeName, found := metricSet.Labels[core.LabelNodename.Key] + if nodeName == "" { + glog.V(8).Infof("Skipping pod %s: no node info", key) + continue + } + if found { + nodeKey := core.NodeKey(nodeName) + node, found := batch.MetricSets[nodeKey] + if !found { + glog.V(1).Info("No metric for node %s, cannot perform node level aggregation.") + } else if err := aggregate(metricSet, node, this.MetricsToAggregate); err != nil { + return nil, err + } + } else { + glog.Errorf("No node info in pod %s: %v", key, metricSet.Labels) + } + } + } + return batch, nil +} diff --git a/vendor/k8s.io/heapster/metrics/processors/node_aggregator_test.go b/vendor/k8s.io/heapster/metrics/processors/node_aggregator_test.go new file mode 100644 index 0000000000..e858ab5d6a --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/node_aggregator_test.go @@ -0,0 +1,94 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func TestNodeAggregate(t *testing.T) { + batch := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodKey("ns1", "pod1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelNamespaceName.Key: "ns1", + core.LabelNodename.Key: "h1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 10, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 222, + }, + }, + }, + + core.PodKey("ns1", "pod2"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelNamespaceName.Key: "ns1", + core.LabelNodename.Key: "h1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 100, + }, + "m3": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 30, + }, + }, + }, + + core.NodeKey("h1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypeNode, + core.LabelNodename.Key: "h1", + }, + MetricValues: map[string]core.MetricValue{}, + }, + }, + } + processor := NodeAggregator{ + MetricsToAggregate: []string{"m1", "m3"}, + } + result, err := processor.Process(&batch) + assert.NoError(t, err) + node, found := result.MetricSets[core.NodeKey("h1")] + assert.True(t, found) + + m1, found := node.MetricValues["m1"] + assert.True(t, found) + assert.Equal(t, 110, m1.IntValue) + + m3, found := node.MetricValues["m3"] + assert.True(t, found) + assert.Equal(t, 30, m3.IntValue) +} diff --git a/vendor/k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go b/vendor/k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go new file mode 100644 index 0000000000..921fc11f1d --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/node_autoscaling_enricher.go @@ -0,0 +1,103 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "net/url" + "time" + + kube_config "k8s.io/heapster/common/kubernetes" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/util" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + kube_client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/fields" +) + +type NodeAutoscalingEnricher struct { + nodeLister *cache.StoreToNodeLister + reflector *cache.Reflector +} + +func (this *NodeAutoscalingEnricher) Name() string { + return "node_autoscaling_enricher" +} + +func (this *NodeAutoscalingEnricher) Process(batch *core.DataBatch) (*core.DataBatch, error) { + nodes, err := this.nodeLister.List() + if err != nil { + return nil, err + } + for _, node := range nodes.Items { + if metricSet, found := batch.MetricSets[core.NodeKey(node.Name)]; found { + metricSet.Labels[core.LabelLabels.Key] = util.LabelsToString(node.Labels, ",") + availableCpu, _ := node.Status.Capacity[kube_api.ResourceCPU] + availableMem, _ := node.Status.Capacity[kube_api.ResourceMemory] + + cpuRequested := getInt(metricSet, &core.MetricCpuRequest) + cpuUsed := getInt(metricSet, &core.MetricCpuUsageRate) + memRequested := getInt(metricSet, &core.MetricMemoryRequest) + memUsed := getInt(metricSet, &core.MetricMemoryUsage) + + if availableCpu.MilliValue() != 0 { + setFloat(metricSet, &core.MetricNodeCpuUtilization, float32(cpuUsed)/float32(availableCpu.MilliValue())) + setFloat(metricSet, &core.MetricNodeCpuReservation, float32(cpuRequested)/float32(availableCpu.MilliValue())) + setFloat(metricSet, &core.MetricNodeCpuCapacity, float32(availableCpu.MilliValue())) + } + + if availableMem.Value() != 0 { + setFloat(metricSet, &core.MetricNodeMemoryUtilization, float32(memUsed)/float32(availableMem.Value())) + setFloat(metricSet, &core.MetricNodeMemoryReservation, float32(memRequested)/float32(availableMem.Value())) + setFloat(metricSet, &core.MetricNodeMemoryCapacity, float32(availableMem.Value())) + } + } + } + return batch, nil +} + +func getInt(metricSet *core.MetricSet, metric *core.Metric) int64 { + if value, found := metricSet.MetricValues[metric.MetricDescriptor.Name]; found { + return value.IntValue + } + return 0 +} + +func setFloat(metricSet *core.MetricSet, metric *core.Metric, value float32) { + metricSet.MetricValues[metric.MetricDescriptor.Name] = core.MetricValue{ + MetricType: core.MetricGauge, + ValueType: core.ValueFloat, + FloatValue: value, + } +} + +func NewNodeAutoscalingEnricher(url *url.URL) (*NodeAutoscalingEnricher, error) { + kubeConfig, err := kube_config.GetKubeClientConfig(url) + if err != nil { + return nil, err + } + kubeClient := kube_client.NewOrDie(kubeConfig) + + // watch nodes + lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything()) + nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)} + reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, time.Hour) + reflector.Run() + + return &NodeAutoscalingEnricher{ + nodeLister: nodeLister, + reflector: reflector, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/processors/pod_aggregator.go b/vendor/k8s.io/heapster/metrics/processors/pod_aggregator.go new file mode 100644 index 0000000000..e7223428e9 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/pod_aggregator.go @@ -0,0 +1,126 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/heapster/metrics/core" +) + +var LabelsToPopulate = []core.LabelDescriptor{ + core.LabelPodId, + core.LabelPodName, + core.LabelPodNamespace, + core.LabelNamespaceName, + core.LabelPodNamespaceUID, + core.LabelHostname, + core.LabelHostID, +} + +type PodAggregator struct { + skippedMetrics map[string]struct{} +} + +func (this *PodAggregator) Name() string { + return "pod_aggregator" +} + +func (this *PodAggregator) Process(batch *core.DataBatch) (*core.DataBatch, error) { + newPods := make(map[string]*core.MetricSet) + + for key, metricSet := range batch.MetricSets { + if metricSetType, found := metricSet.Labels[core.LabelMetricSetType.Key]; found && metricSetType == core.MetricSetTypePodContainer { + // Aggregating containers + podName, found := metricSet.Labels[core.LabelPodName.Key] + ns, found2 := metricSet.Labels[core.LabelNamespaceName.Key] + if found && found2 { + podKey := core.PodKey(ns, podName) + pod, found := batch.MetricSets[podKey] + if !found { + pod, found = newPods[podKey] + if !found { + glog.V(2).Infof("Pod not found adding %s", podKey) + pod = this.podMetricSet(metricSet.Labels) + newPods[podKey] = pod + } + } + + for metricName, metricValue := range metricSet.MetricValues { + if _, found := this.skippedMetrics[metricName]; found { + continue + } + + aggregatedValue, found := pod.MetricValues[metricName] + if found { + if aggregatedValue.ValueType != metricValue.ValueType { + glog.Errorf("PodAggregator: inconsistent type in %s", metricName) + continue + } + + switch aggregatedValue.ValueType { + case core.ValueInt64: + aggregatedValue.IntValue += metricValue.IntValue + case core.ValueFloat: + aggregatedValue.FloatValue += metricValue.FloatValue + default: + return nil, fmt.Errorf("PodAggregator: type not supported in %s", metricName) + } + } else { + aggregatedValue = metricValue + } + pod.MetricValues[metricName] = aggregatedValue + } + } else { + glog.Errorf("No namespace and/or pod info in container %s: %v", key, metricSet.Labels) + continue + } + } + } + for key, val := range newPods { + batch.MetricSets[key] = val + } + return batch, nil +} + +func (this *PodAggregator) podMetricSet(labels map[string]string) *core.MetricSet { + newLabels := map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + } + for _, l := range LabelsToPopulate { + if val, ok := labels[l.Key]; ok { + newLabels[l.Key] = val + } + } + return &core.MetricSet{ + MetricValues: make(map[string]core.MetricValue), + Labels: newLabels, + } +} + +func NewPodAggregator() *PodAggregator { + skipped := make(map[string]struct{}) + for _, metric := range core.StandardMetrics { + if metric.MetricDescriptor.Type == core.MetricCumulative || + metric.MetricDescriptor.Type == core.MetricDelta { + skipped[metric.MetricDescriptor.Name] = struct{}{} + } + } + return &PodAggregator{ + skippedMetrics: skipped, + } +} diff --git a/vendor/k8s.io/heapster/metrics/processors/pod_aggregator_test.go b/vendor/k8s.io/heapster/metrics/processors/pod_aggregator_test.go new file mode 100644 index 0000000000..49231adc42 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/pod_aggregator_test.go @@ -0,0 +1,96 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func TestPodAggregator(t *testing.T) { + batch := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodContainerKey("ns1", "pod1", "c1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + core.LabelPodName.Key: "pod1", + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 10, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 222, + }, + }, + }, + + core.PodContainerKey("ns1", "pod1", "c2"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + core.LabelPodName.Key: "pod1", + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 100, + }, + "m3": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 30, + }, + }, + }, + }, + } + processor := PodAggregator{} + result, err := processor.Process(&batch) + assert.NoError(t, err) + pod, found := result.MetricSets[core.PodKey("ns1", "pod1")] + assert.True(t, found) + + m1, found := pod.MetricValues["m1"] + assert.True(t, found) + assert.Equal(t, 110, m1.IntValue) + + m2, found := pod.MetricValues["m2"] + assert.True(t, found) + assert.Equal(t, 222, m2.IntValue) + + m3, found := pod.MetricValues["m3"] + assert.True(t, found) + assert.Equal(t, 30, m3.IntValue) + + labelPodName, found := pod.Labels[core.LabelPodName.Key] + assert.True(t, found) + assert.Equal(t, "pod1", labelPodName) + + labelNsName, found := pod.Labels[core.LabelNamespaceName.Key] + assert.True(t, found) + assert.Equal(t, "ns1", labelNsName) +} diff --git a/vendor/k8s.io/heapster/metrics/processors/pod_based_enricher.go b/vendor/k8s.io/heapster/metrics/processors/pod_based_enricher.go new file mode 100644 index 0000000000..5f71d438cc --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/pod_based_enricher.go @@ -0,0 +1,203 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/heapster/metrics/util" + + "k8s.io/heapster/metrics/core" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" +) + +type PodBasedEnricher struct { + podLister *cache.StoreToPodLister +} + +func (this *PodBasedEnricher) Name() string { + return "pod_based_enricher" +} + +func (this *PodBasedEnricher) Process(batch *core.DataBatch) (*core.DataBatch, error) { + newMs := make(map[string]*core.MetricSet, len(batch.MetricSets)) + for k, v := range batch.MetricSets { + switch v.Labels[core.LabelMetricSetType.Key] { + case core.MetricSetTypePod: + namespace := v.Labels[core.LabelNamespaceName.Key] + podName := v.Labels[core.LabelPodName.Key] + pod, err := this.getPod(namespace, podName) + if err != nil { + glog.V(3).Infof("Failed to get pod %s from cache: %v", core.PodKey(namespace, podName), err) + continue + } + addPodInfo(k, v, pod, batch, newMs) + case core.MetricSetTypePodContainer: + namespace := v.Labels[core.LabelNamespaceName.Key] + podName := v.Labels[core.LabelPodName.Key] + pod, err := this.getPod(namespace, podName) + if err != nil { + glog.V(3).Infof("Failed to get pod %s from cache: %v", core.PodKey(namespace, podName), err) + continue + } + addContainerInfo(k, v, pod, batch, newMs) + } + } + for k, v := range newMs { + batch.MetricSets[k] = v + } + return batch, nil +} + +func (this *PodBasedEnricher) getPod(namespace, name string) (*kube_api.Pod, error) { + o, exists, err := this.podLister.Get( + &kube_api.Pod{ + ObjectMeta: kube_api.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + }, + ) + if err != nil { + return nil, err + } + if !exists || o == nil { + return nil, fmt.Errorf("cannot find pod definition") + } + pod, ok := o.(*kube_api.Pod) + if !ok { + return nil, fmt.Errorf("cache contains wrong type") + } + return pod, nil +} + +func addContainerInfo(key string, containerMs *core.MetricSet, pod *kube_api.Pod, batch *core.DataBatch, newMs map[string]*core.MetricSet) { + for _, container := range pod.Spec.Containers { + if key == core.PodContainerKey(pod.Namespace, pod.Name, container.Name) { + updateContainerResourcesAndLimits(containerMs, container) + if _, ok := containerMs.Labels[core.LabelContainerBaseImage.Key]; !ok { + containerMs.Labels[core.LabelContainerBaseImage.Key] = container.Image + } + break + } + } + + containerMs.Labels[core.LabelPodId.Key] = string(pod.UID) + containerMs.Labels[core.LabelLabels.Key] = util.LabelsToString(pod.Labels, ",") + + namespace := containerMs.Labels[core.LabelNamespaceName.Key] + podName := containerMs.Labels[core.LabelPodName.Key] + + podKey := core.PodKey(namespace, podName) + _, oldfound := batch.MetricSets[podKey] + if !oldfound { + _, newfound := batch.MetricSets[podKey] + if !newfound { + glog.V(2).Infof("Pod %s not found, creating a stub", podKey) + podMs := &core.MetricSet{ + MetricValues: make(map[string]core.MetricValue), + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelNamespaceName.Key: namespace, + core.LabelPodNamespace.Key: namespace, + core.LabelPodName.Key: podName, + core.LabelNodename.Key: containerMs.Labels[core.LabelNodename.Key], + core.LabelHostname.Key: containerMs.Labels[core.LabelHostname.Key], + core.LabelHostID.Key: containerMs.Labels[core.LabelHostID.Key], + }, + } + newMs[podKey] = podMs + addPodInfo(podKey, podMs, pod, batch, newMs) + } + } +} + +func addPodInfo(key string, podMs *core.MetricSet, pod *kube_api.Pod, batch *core.DataBatch, newMs map[string]*core.MetricSet) { + + // Add UID to pod + podMs.Labels[core.LabelPodId.Key] = string(pod.UID) + podMs.Labels[core.LabelLabels.Key] = util.LabelsToString(pod.Labels, ",") + + // Add cpu/mem requests and limits to containers + for _, container := range pod.Spec.Containers { + containerKey := core.PodContainerKey(pod.Namespace, pod.Name, container.Name) + if _, found := batch.MetricSets[containerKey]; !found { + if _, found := newMs[containerKey]; !found { + glog.V(2).Infof("Container %s not found, creating a stub", containerKey) + containerMs := &core.MetricSet{ + MetricValues: make(map[string]core.MetricValue), + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + core.LabelNamespaceName.Key: pod.Namespace, + core.LabelPodNamespace.Key: pod.Namespace, + core.LabelPodName.Key: pod.Name, + core.LabelContainerName.Key: container.Name, + core.LabelContainerBaseImage.Key: container.Image, + core.LabelPodId.Key: string(pod.UID), + core.LabelLabels.Key: util.LabelsToString(pod.Labels, ","), + core.LabelNodename.Key: podMs.Labels[core.LabelNodename.Key], + core.LabelHostname.Key: podMs.Labels[core.LabelHostname.Key], + core.LabelHostID.Key: podMs.Labels[core.LabelHostID.Key], + }, + } + updateContainerResourcesAndLimits(containerMs, container) + newMs[containerKey] = containerMs + } + } + } +} + +func updateContainerResourcesAndLimits(metricSet *core.MetricSet, container kube_api.Container) { + requests := container.Resources.Requests + if val, found := requests[kube_api.ResourceCPU]; found { + metricSet.MetricValues[core.MetricCpuRequest.Name] = intValue(val.MilliValue()) + } else { + metricSet.MetricValues[core.MetricCpuRequest.Name] = intValue(0) + } + if val, found := requests[kube_api.ResourceMemory]; found { + metricSet.MetricValues[core.MetricMemoryRequest.Name] = intValue(val.Value()) + } else { + metricSet.MetricValues[core.MetricMemoryRequest.Name] = intValue(0) + } + + limits := container.Resources.Limits + if val, found := limits[kube_api.ResourceCPU]; found { + metricSet.MetricValues[core.MetricCpuLimit.Name] = intValue(val.MilliValue()) + } else { + metricSet.MetricValues[core.MetricCpuLimit.Name] = intValue(0) + } + if val, found := limits[kube_api.ResourceMemory]; found { + metricSet.MetricValues[core.MetricMemoryLimit.Name] = intValue(val.Value()) + } else { + metricSet.MetricValues[core.MetricMemoryLimit.Name] = intValue(0) + } +} + +func intValue(value int64) core.MetricValue { + return core.MetricValue{ + IntValue: value, + MetricType: core.MetricGauge, + ValueType: core.ValueInt64, + } +} + +func NewPodBasedEnricher(podLister *cache.StoreToPodLister) (*PodBasedEnricher, error) { + return &PodBasedEnricher{ + podLister: podLister, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/processors/pod_based_enricher_test.go b/vendor/k8s.io/heapster/metrics/processors/pod_based_enricher_test.go new file mode 100644 index 0000000000..a1b9a75db0 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/pod_based_enricher_test.go @@ -0,0 +1,164 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" + + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/resource" + "k8s.io/kubernetes/pkg/client/cache" +) + +var batches = []*core.DataBatch{ + &core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodContainerKey("ns1", "pod1", "c1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + core.LabelPodName.Key: "pod1", + core.LabelNamespaceName.Key: "ns1", + core.LabelContainerName.Key: "c1", + }, + MetricValues: map[string]core.MetricValue{}, + }, + + core.PodKey("ns1", "pod1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodName.Key: "pod1", + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{}, + }, + }, + }, + &core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodContainerKey("ns1", "pod1", "c1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + core.LabelPodName.Key: "pod1", + core.LabelNamespaceName.Key: "ns1", + core.LabelContainerName.Key: "c1", + }, + MetricValues: map[string]core.MetricValue{}, + }, + }, + }, + &core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + core.PodKey("ns1", "pod1"): { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodName.Key: "pod1", + core.LabelNamespaceName.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{}, + }, + }, + }, +} + +func TestPodEnricher(t *testing.T) { + pod := kube_api.Pod{ + ObjectMeta: kube_api.ObjectMeta{ + Name: "pod1", + Namespace: "ns1", + }, + Spec: kube_api.PodSpec{ + NodeName: "node1", + Containers: []kube_api.Container{ + { + Name: "c1", + Image: "gcr.io/google_containers/pause:2.0", + Resources: kube_api.ResourceRequirements{ + Requests: kube_api.ResourceList{ + kube_api.ResourceCPU: *resource.NewMilliQuantity(100, resource.DecimalSI), + kube_api.ResourceMemory: *resource.NewQuantity(555, resource.DecimalSI), + }, + }, + }, + { + Name: "nginx", + Image: "gcr.io/google_containers/pause:2.0", + Resources: kube_api.ResourceRequirements{ + Requests: kube_api.ResourceList{ + kube_api.ResourceCPU: *resource.NewMilliQuantity(333, resource.DecimalSI), + kube_api.ResourceMemory: *resource.NewQuantity(1000, resource.DecimalSI), + }, + Limits: kube_api.ResourceList{ + kube_api.ResourceCPU: *resource.NewMilliQuantity(2222, resource.DecimalSI), + kube_api.ResourceMemory: *resource.NewQuantity(3333, resource.DecimalSI), + }, + }, + }, + }, + }, + } + + store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + podLister := &cache.StoreToPodLister{Indexer: store} + podLister.Add(&pod) + podBasedEnricher := PodBasedEnricher{podLister: podLister} + + var err error + for _, batch := range batches { + batch, err = podBasedEnricher.Process(batch) + assert.NoError(t, err) + + podAggregator := PodAggregator{} + batch, err = podAggregator.Process(batch) + assert.NoError(t, err) + + podMs, found := batch.MetricSets[core.PodKey("ns1", "pod1")] + assert.True(t, found) + checkRequests(t, podMs, 433, 1555) + checkLimits(t, podMs, 2222, 3333) + + containerMs, found := batch.MetricSets[core.PodContainerKey("ns1", "pod1", "c1")] + assert.True(t, found) + checkRequests(t, containerMs, 100, 555) + checkLimits(t, containerMs, 0, 0) + } +} + +func checkRequests(t *testing.T, ms *core.MetricSet, cpu, mem int64) { + cpuVal, found := ms.MetricValues[core.MetricCpuRequest.Name] + assert.True(t, found) + assert.Equal(t, cpu, cpuVal.IntValue) + + memVal, found := ms.MetricValues[core.MetricMemoryRequest.Name] + assert.True(t, found) + assert.Equal(t, mem, memVal.IntValue) +} + +func checkLimits(t *testing.T, ms *core.MetricSet, cpu, mem int64) { + cpuVal, found := ms.MetricValues[core.MetricCpuLimit.Name] + assert.True(t, found) + assert.Equal(t, cpu, cpuVal.IntValue) + + memVal, found := ms.MetricValues[core.MetricMemoryLimit.Name] + assert.True(t, found) + assert.Equal(t, mem, memVal.IntValue) +} diff --git a/vendor/k8s.io/heapster/metrics/processors/rate_calculator.go b/vendor/k8s.io/heapster/metrics/processors/rate_calculator.go new file mode 100644 index 0000000000..5682f12cd0 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/rate_calculator.go @@ -0,0 +1,93 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" +) + +type RateCalculator struct { + rateMetricsMapping map[string]core.Metric + previousBatch *core.DataBatch +} + +func (this *RateCalculator) Name() string { + return "rate calculator" +} + +func (this *RateCalculator) Process(batch *core.DataBatch) (*core.DataBatch, error) { + if this.previousBatch == nil { + this.previousBatch = batch + return batch, nil + } + if !batch.Timestamp.After(this.previousBatch.Timestamp) { + // something got out of sync, do nothing. + glog.Errorf("New data batch has timestamp before the previous one: new:%v old:%v", batch.Timestamp, this.previousBatch.Timestamp) + return batch, nil + } + + for key, newMs := range batch.MetricSets { + + if oldMs, found := this.previousBatch.MetricSets[key]; found { + if !newMs.ScrapeTime.After(oldMs.ScrapeTime) { + // New must be strictly after old. + continue + } + if !newMs.CreateTime.Equal(oldMs.CreateTime) { + glog.V(4).Infof("Skipping rates for %s - different create time new:%v old:%v", key, newMs.CreateTime, oldMs.CreateTime) + // Create time for container must be the same. + continue + } + + for metricName, targetMetric := range this.rateMetricsMapping { + metricValNew, foundNew := newMs.MetricValues[metricName] + metricValOld, foundOld := oldMs.MetricValues[metricName] + if foundNew && foundOld { + if metricName == core.MetricCpuUsage.MetricDescriptor.Name { + // cpu/usage values are in nanoseconds; we want to have it in millicores (that's why constant 1000 is here). + newVal := 1000 * (metricValNew.IntValue - metricValOld.IntValue) / + (newMs.ScrapeTime.UnixNano() - oldMs.ScrapeTime.UnixNano()) + + newMs.MetricValues[targetMetric.MetricDescriptor.Name] = core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: newVal, + } + + } else if targetMetric.MetricDescriptor.ValueType == core.ValueFloat { + newVal := 1e9 * float32(metricValNew.IntValue-metricValOld.IntValue) / + float32(newMs.ScrapeTime.UnixNano()-oldMs.ScrapeTime.UnixNano()) + + newMs.MetricValues[targetMetric.MetricDescriptor.Name] = core.MetricValue{ + ValueType: core.ValueFloat, + MetricType: core.MetricGauge, + FloatValue: newVal, + } + } + } + } + } + } + this.previousBatch = batch + return batch, nil +} + +func NewRateCalculator(metrics map[string]core.Metric) *RateCalculator { + return &RateCalculator{ + rateMetricsMapping: metrics, + } +} diff --git a/vendor/k8s.io/heapster/metrics/processors/rate_calculator_test.go b/vendor/k8s.io/heapster/metrics/processors/rate_calculator_test.go new file mode 100644 index 0000000000..9bbfbd10fd --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/processors/rate_calculator_test.go @@ -0,0 +1,93 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package processors + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func TestRateCalculator(t *testing.T) { + key := core.PodContainerKey("ns1", "pod1", "c") + now := time.Now() + + prev := &core.DataBatch{ + Timestamp: now.Add(-time.Minute), + MetricSets: map[string]*core.MetricSet{ + key: { + CreateTime: now.Add(-time.Hour), + ScrapeTime: now.Add(-60 * time.Second), + + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + }, + MetricValues: map[string]core.MetricValue{ + core.MetricCpuUsage.MetricDescriptor.Name: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 947130377781, + }, + core.MetricNetworkTxErrors.MetricDescriptor.Name: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 0, + }, + }, + }, + }, + } + + current := &core.DataBatch{ + Timestamp: now, + MetricSets: map[string]*core.MetricSet{ + + key: { + CreateTime: now.Add(-time.Hour), + ScrapeTime: now, + + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePodContainer, + }, + MetricValues: map[string]core.MetricValue{ + core.MetricCpuUsage.MetricDescriptor.Name: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 948071062732, + }, + core.MetricNetworkTxErrors.MetricDescriptor.Name: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 120, + }, + }, + }, + }, + } + + procesor := NewRateCalculator(core.RateMetricsMapping) + procesor.Process(prev) + procesor.Process(current) + + ms := current.MetricSets[key] + cpuRate := ms.MetricValues[core.MetricCpuUsageRate.Name] + txeRate := ms.MetricValues[core.MetricNetworkTxErrorsRate.Name] + + assert.InEpsilon(t, 13, cpuRate.IntValue, 2) + assert.InEpsilon(t, 2, txeRate.FloatValue, 0.1) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver.go b/vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver.go new file mode 100644 index 0000000000..8df8aa8567 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver.go @@ -0,0 +1,104 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package elasticsearch + +import ( + "net/url" + "sync" + "time" + + "github.com/golang/glog" + "github.com/olivere/elastic" + esCommon "k8s.io/heapster/common/elasticsearch" + "k8s.io/heapster/metrics/core" +) + +const ( + typeName = "k8s-heapster" +) + +// SaveDataFunc is a pluggable function to enforce limits on the object +type SaveDataFunc func(esClient *elastic.Client, indexName string, typeName string, sinkData interface{}) error + +type elasticSearchSink struct { + saveDataFunc SaveDataFunc + esConfig esCommon.ElasticSearchConfig + sync.RWMutex +} + +type EsSinkPoint struct { + MetricsName string + MetricsValue interface{} + MetricsTimestamp time.Time + MetricsTags map[string]string +} + +func (sink *elasticSearchSink) ExportData(dataBatch *core.DataBatch) { + sink.Lock() + defer sink.Unlock() + for _, metricSet := range dataBatch.MetricSets { + for metricName, metricValue := range metricSet.MetricValues { + point := EsSinkPoint{ + MetricsName: metricName, + MetricsTags: metricSet.Labels, + MetricsValue: map[string]interface{}{ + "value": metricValue.GetValue(), + }, + MetricsTimestamp: dataBatch.Timestamp.UTC(), + } + sink.saveDataFunc(sink.esConfig.EsClient, sink.esConfig.Index, typeName, point) + } + for _, metric := range metricSet.LabeledMetrics { + labels := make(map[string]string) + for k, v := range metricSet.Labels { + labels[k] = v + } + for k, v := range metric.Labels { + labels[k] = v + } + point := EsSinkPoint{ + MetricsName: metric.Name, + MetricsTags: labels, + MetricsValue: map[string]interface{}{ + "value": metric.GetValue(), + }, + MetricsTimestamp: dataBatch.Timestamp.UTC(), + } + sink.saveDataFunc(sink.esConfig.EsClient, sink.esConfig.Index, typeName, point) + } + } +} + +func (sink *elasticSearchSink) Name() string { + return "ElasticSearch Sink" +} + +func (sink *elasticSearchSink) Stop() { + // nothing needs to be done. +} + +func NewElasticSearchSink(uri *url.URL) (core.DataSink, error) { + var esSink elasticSearchSink + elasticsearchConfig, err := esCommon.CreateElasticSearchConfig(uri) + if err != nil { + glog.V(2).Infof("failed to config elasticsearch") + return nil, err + } + + esSink.esConfig = *elasticsearchConfig + esSink.saveDataFunc = esCommon.SaveDataIntoES + glog.V(2).Infof("elasticsearch sink setup successfully") + return &esSink, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver_test.go b/vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver_test.go new file mode 100644 index 0000000000..8abc9f6aa7 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/elasticsearch/driver_test.go @@ -0,0 +1,187 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package elasticsearch + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/olivere/elastic" + "github.com/stretchr/testify/assert" + esCommon "k8s.io/heapster/common/elasticsearch" + "k8s.io/heapster/metrics/core" +) + +type dataSavedToES struct { + data string +} + +type fakeESSink struct { + core.DataSink + savedData []dataSavedToES +} + +var FakeESSink fakeESSink + +func SaveDataIntoES_Stub(esClient *elastic.Client, indexName string, typeName string, sinkData interface{}) error { + jsonItems, err := json.Marshal(sinkData) + if err != nil { + return fmt.Errorf("failed to transform the items to json : %s", err) + } + FakeESSink.savedData = append(FakeESSink.savedData, dataSavedToES{string(jsonItems)}) + return nil +} + +// Returns a fake ES sink. +func NewFakeSink() fakeESSink { + savedData := make([]dataSavedToES, 0) + return fakeESSink{ + &elasticSearchSink{ + saveDataFunc: SaveDataIntoES_Stub, + esConfig: esCommon.ElasticSearchConfig{ + Index: "heapster-metric-index", + EsClient: &elastic.Client{}, + }, + }, + savedData, + } +} + +func TestStoreDataEmptyInput(t *testing.T) { + fakeSink := NewFakeSink() + dataBatch := core.DataBatch{} + fakeSink.ExportData(&dataBatch) + assert.Equal(t, 0, len(fakeSink.savedData)) +} + +func TestStoreMultipleDataInput(t *testing.T) { + fakeSink := NewFakeSink() + timestamp := time.Now() + + l := make(map[string]string) + l["namespace_id"] = "123" + l["container_name"] = "/system.slice/-.mount" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l2 := make(map[string]string) + l2["namespace_id"] = "123" + l2["container_name"] = "/system.slice/dbus.service" + l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l3 := make(map[string]string) + l3["namespace_id"] = "123" + l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l4 := make(map[string]string) + l4["namespace_id"] = "" + l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l5 := make(map[string]string) + l5["namespace_id"] = "123" + l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + metricSet1 := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + "/system.slice/-.mount//cpu/limit": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet2 := core.MetricSet{ + Labels: l2, + MetricValues: map[string]core.MetricValue{ + "/system.slice/dbus.service//cpu/usage": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet3 := core.MetricSet{ + Labels: l3, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet4 := core.MetricSet{ + Labels: l4, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet5 := core.MetricSet{ + Labels: l5, + MetricValues: map[string]core.MetricValue{ + "removeme": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + data := core.DataBatch{ + Timestamp: timestamp, + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet1, + "pod2": &metricSet2, + "pod3": &metricSet3, + "pod4": &metricSet4, + "pod5": &metricSet5, + }, + } + + timeStr, err := timestamp.UTC().MarshalJSON() + assert.NoError(t, err) + + fakeSink.ExportData(&data) + + //expect msg string + assert.Equal(t, 5, len(FakeESSink.savedData)) + + var expectMsgTemplate = [5]string{ + `{"MetricsName":"/system.slice/-.mount//cpu/limit","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"container_name":"/system.slice/-.mount","namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"/system.slice/dbus.service//cpu/usage","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"container_name":"/system.slice/dbus.service","namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"test/metric/1","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"test/metric/1","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"namespace_id":"","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"removeme","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + } + + msgsString := fmt.Sprintf("%s", FakeESSink.savedData) + + for _, mgsTemplate := range expectMsgTemplate { + expectMsg := fmt.Sprintf(mgsTemplate, timeStr) + assert.Contains(t, msgsString, expectMsg) + } + + FakeESSink = fakeESSink{} +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/factory.go b/vendor/k8s.io/heapster/metrics/sinks/factory.go new file mode 100644 index 0000000000..13cdcd241e --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/factory.go @@ -0,0 +1,109 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sinks + +import ( + "fmt" + "time" + + "github.com/golang/glog" + "k8s.io/heapster/common/flags" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/sinks/elasticsearch" + "k8s.io/heapster/metrics/sinks/gcm" + "k8s.io/heapster/metrics/sinks/hawkular" + "k8s.io/heapster/metrics/sinks/influxdb" + "k8s.io/heapster/metrics/sinks/kafka" + "k8s.io/heapster/metrics/sinks/log" + "k8s.io/heapster/metrics/sinks/metric" + "k8s.io/heapster/metrics/sinks/monasca" + "k8s.io/heapster/metrics/sinks/opentsdb" + "k8s.io/heapster/metrics/sinks/riemann" +) + +type SinkFactory struct { +} + +func (this *SinkFactory) Build(uri flags.Uri) (core.DataSink, error) { + switch uri.Key { + case "gcm": + return gcm.CreateGCMSink(&uri.Val) + case "hawkular": + return hawkular.NewHawkularSink(&uri.Val) + case "influxdb": + return influxdb.CreateInfluxdbSink(&uri.Val) + case "kafka": + return kafka.NewKafkaSink(&uri.Val) + case "log": + return logsink.NewLogSink(), nil + case "metric": + return metricsink.NewMetricSink(140*time.Second, 15*time.Minute, []string{ + core.MetricCpuUsageRate.MetricDescriptor.Name, + core.MetricMemoryUsage.MetricDescriptor.Name}), nil + case "monasca": + return monasca.CreateMonascaSink(&uri.Val) + case "riemann": + return riemann.CreateRiemannSink(&uri.Val) + case "opentsdb": + return opentsdb.CreateOpenTSDBSink(&uri.Val) + case "elasticsearch": + return elasticsearch.NewElasticSearchSink(&uri.Val) + default: + return nil, fmt.Errorf("Sink not recognized: %s", uri.Key) + } +} + +func (this *SinkFactory) BuildAll(uris flags.Uris, historicalUri string) (*metricsink.MetricSink, []core.DataSink, core.HistoricalSource) { + result := make([]core.DataSink, 0, len(uris)) + var metric *metricsink.MetricSink + var historical core.HistoricalSource + for _, uri := range uris { + sink, err := this.Build(uri) + if err != nil { + glog.Errorf("Failed to create sink: %v", err) + continue + } + if uri.Key == "metric" { + metric = sink.(*metricsink.MetricSink) + } + if uri.String() == historicalUri { + if asHistSource, ok := sink.(core.AsHistoricalSource); ok { + historical = asHistSource.Historical() + } else { + glog.Errorf("Sink type %q does not support being used for historical access", uri.Key) + } + } + result = append(result, sink) + } + if metric == nil { + uri := flags.Uri{} + uri.Set("metric") + sink, err := this.Build(uri) + if err == nil { + result = append(result, sink) + metric = sink.(*metricsink.MetricSink) + } else { + glog.Errorf("Error while creating metric sink: %v", err) + } + } + if len(historicalUri) > 0 && historical == nil { + glog.Errorf("Error while initializing historical access: unable to use sink %q as a historical source", historicalUri) + } + return metric, result, historical +} + +func NewSinkFactory() *SinkFactory { + return &SinkFactory{} +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/gcm/gcm.go b/vendor/k8s.io/heapster/metrics/sinks/gcm/gcm.go new file mode 100644 index 0000000000..8ab5c24f41 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/gcm/gcm.go @@ -0,0 +1,341 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gcm + +import ( + "fmt" + "net/url" + "strings" + "sync" + "time" + + gce_util "k8s.io/heapster/common/gce" + "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + gcm "google.golang.org/api/cloudmonitoring/v2beta2" + gce "google.golang.org/cloud/compute/metadata" +) + +const ( + metricDomain = "kubernetes.io" + customApiPrefix = "custom.cloudmonitoring.googleapis.com" + maxNumLabels = 10 + // The largest number of timeseries we can write to per request. + maxTimeseriesPerRequest = 200 +) + +type MetricFilter int8 + +const ( + metricsAll MetricFilter = iota + metricsOnlyAutoscaling +) + +type gcmSink struct { + sync.RWMutex + registered bool + project string + metricFilter MetricFilter + gcmService *gcm.Service +} + +func (sink *gcmSink) Name() string { + return "GCM Sink" +} + +func getReq() *gcm.WriteTimeseriesRequest { + return &gcm.WriteTimeseriesRequest{Timeseries: make([]*gcm.TimeseriesPoint, 0)} +} + +func fullLabelName(name string) string { + // handle correctly GCE specific labels + if !strings.Contains(name, "compute.googleapis.com") { + return fmt.Sprintf("%s/%s/label/%s", customApiPrefix, metricDomain, name) + } + return name +} + +func fullMetricName(name string) string { + return fmt.Sprintf("%s/%s/%s", customApiPrefix, metricDomain, name) +} + +func (sink *gcmSink) getTimeseriesPoint(timestamp time.Time, labels map[string]string, metric string, val core.MetricValue, createTime time.Time) *gcm.TimeseriesPoint { + point := &gcm.Point{ + Start: timestamp.Format(time.RFC3339), + End: timestamp.Format(time.RFC3339), + } + switch val.ValueType { + case core.ValueInt64: + point.Int64Value = &val.IntValue + case core.ValueFloat: + v := float64(val.FloatValue) + point.DoubleValue = &v + default: + glog.Errorf("Type not supported %v in %v", val.ValueType, metric) + return nil + } + // For cumulative metric use the provided start time. + if val.MetricType == core.MetricCumulative { + point.Start = createTime.Format(time.RFC3339) + } + + finalLabels := make(map[string]string) + if core.IsNodeAutoscalingMetric(metric) { + // All and autoscaling. Do not populate for other filters. + if sink.metricFilter != metricsAll && + sink.metricFilter != metricsOnlyAutoscaling { + return nil + } + + finalLabels[fullLabelName(core.LabelHostname.Key)] = labels[core.LabelHostname.Key] + finalLabels[fullLabelName(core.LabelGCEResourceID.Key)] = labels[core.LabelHostID.Key] + finalLabels[fullLabelName(core.LabelGCEResourceType.Key)] = "instance" + } else { + // Only all. + if sink.metricFilter != metricsAll { + return nil + } + supportedLables := core.GcmLabels() + for key, value := range labels { + if _, ok := supportedLables[key]; ok { + finalLabels[fullLabelName(key)] = value + } + } + } + desc := &gcm.TimeseriesDescriptor{ + Project: sink.project, + Labels: finalLabels, + Metric: fullMetricName(metric), + } + + return &gcm.TimeseriesPoint{Point: point, TimeseriesDesc: desc} +} + +func (sink *gcmSink) getTimeseriesPointForLabeledMetrics(timestamp time.Time, labels map[string]string, metric core.LabeledMetric, createTime time.Time) *gcm.TimeseriesPoint { + // Only all. There are no atuoscaling labeled metrics. + if sink.metricFilter != metricsAll { + return nil + } + + point := &gcm.Point{ + Start: timestamp.Format(time.RFC3339), + End: timestamp.Format(time.RFC3339), + } + switch metric.ValueType { + case core.ValueInt64: + point.Int64Value = &metric.IntValue + case core.ValueFloat: + v := float64(metric.FloatValue) + point.DoubleValue = &v + default: + glog.Errorf("Type not supported %v in %v", metric.ValueType, metric) + return nil + } + // For cumulative metric use the provided start time. + if metric.MetricType == core.MetricCumulative { + point.Start = createTime.Format(time.RFC3339) + } + + finalLabels := make(map[string]string) + supportedLables := core.GcmLabels() + for key, value := range labels { + if _, ok := supportedLables[key]; ok { + finalLabels[fullLabelName(key)] = value + } + } + for key, value := range metric.Labels { + if _, ok := supportedLables[key]; ok { + finalLabels[fullLabelName(key)] = value + } + } + + desc := &gcm.TimeseriesDescriptor{ + Project: sink.project, + Labels: finalLabels, + Metric: fullMetricName(metric.Name), + } + + return &gcm.TimeseriesPoint{Point: point, TimeseriesDesc: desc} +} + +func (sink *gcmSink) sendRequest(req *gcm.WriteTimeseriesRequest) { + _, err := sink.gcmService.Timeseries.Write(sink.project, req).Do() + if err != nil { + glog.Errorf("Error while sending request to GCM %v", err) + } else { + glog.V(4).Infof("Successfully sent %v timeserieses to GCM", len(req.Timeseries)) + } +} + +func (sink *gcmSink) ExportData(dataBatch *core.DataBatch) { + if err := sink.registerAllMetrics(); err != nil { + glog.Warningf("Error during metrics registration: %v", err) + return + } + + req := getReq() + for _, metricSet := range dataBatch.MetricSets { + for metric, val := range metricSet.MetricValues { + point := sink.getTimeseriesPoint(dataBatch.Timestamp, metricSet.Labels, metric, val, metricSet.CreateTime) + if point != nil { + req.Timeseries = append(req.Timeseries, point) + } + if len(req.Timeseries) >= maxTimeseriesPerRequest { + sink.sendRequest(req) + req = getReq() + } + } + for _, metric := range metricSet.LabeledMetrics { + point := sink.getTimeseriesPointForLabeledMetrics(dataBatch.Timestamp, metricSet.Labels, metric, metricSet.CreateTime) + if point != nil { + req.Timeseries = append(req.Timeseries, point) + } + if len(req.Timeseries) >= maxTimeseriesPerRequest { + sink.sendRequest(req) + req = getReq() + } + } + } + if len(req.Timeseries) > 0 { + sink.sendRequest(req) + } +} + +func (sink *gcmSink) Stop() { + // nothing needs to be done. +} + +func (sink *gcmSink) registerAllMetrics() error { + return sink.register(core.AllMetrics) +} + +// Adds the specified metrics or updates them if they already exist. +func (sink *gcmSink) register(metrics []core.Metric) error { + sink.Lock() + defer sink.Unlock() + if sink.registered { + return nil + } + + for _, metric := range metrics { + metricName := fullMetricName(metric.MetricDescriptor.Name) + if _, err := sink.gcmService.MetricDescriptors.Delete(sink.project, metricName).Do(); err != nil { + glog.Infof("[GCM] Deleting metric %v failed: %v", metricName, err) + } + labels := make([]*gcm.MetricDescriptorLabelDescriptor, 0) + + // Node autoscaling metrics have special labels. + if core.IsNodeAutoscalingMetric(metric.MetricDescriptor.Name) { + // All and autoscaling. Do not populate for other filters. + if sink.metricFilter != metricsAll && + sink.metricFilter != metricsOnlyAutoscaling { + continue + } + + for _, l := range core.GcmNodeAutoscalingLabels() { + labels = append(labels, &gcm.MetricDescriptorLabelDescriptor{ + Key: fullLabelName(l.Key), + Description: l.Description, + }) + } + } else { + // Only all. + if sink.metricFilter != metricsAll { + continue + } + + for _, l := range core.GcmLabels() { + labels = append(labels, &gcm.MetricDescriptorLabelDescriptor{ + Key: fullLabelName(l.Key), + Description: l.Description, + }) + } + } + + t := &gcm.MetricDescriptorTypeDescriptor{ + MetricType: metric.MetricDescriptor.Type.String(), + ValueType: metric.MetricDescriptor.ValueType.String(), + } + desc := &gcm.MetricDescriptor{ + Name: metricName, + Project: sink.project, + Description: metric.MetricDescriptor.Description, + Labels: labels, + TypeDescriptor: t, + } + if _, err := sink.gcmService.MetricDescriptors.Create(sink.project, desc).Do(); err != nil { + return err + } + } + sink.registered = true + return nil +} + +func CreateGCMSink(uri *url.URL) (core.DataSink, error) { + if len(uri.Scheme) > 0 { + return nil, fmt.Errorf("scheme should not be set for GCM sink") + } + if len(uri.Host) > 0 { + return nil, fmt.Errorf("host should not be set for GCM sink") + } + + opts, err := url.ParseQuery(uri.RawQuery) + + metrics := "all" + if len(opts["metrics"]) > 0 { + metrics = opts["metrics"][0] + } + var metricFilter MetricFilter = metricsAll + switch metrics { + case "all": + metricFilter = metricsAll + case "autoscaling": + metricFilter = metricsOnlyAutoscaling + default: + return nil, fmt.Errorf("invalid metrics parameter: %s", metrics) + } + + if err := gce_util.EnsureOnGCE(); err != nil { + return nil, err + } + + // Detect project ID + projectId, err := gce.ProjectID() + if err != nil { + return nil, err + } + + // Create Google Cloud Monitoring service. + client := oauth2.NewClient(oauth2.NoContext, google.ComputeTokenSource("")) + gcmService, err := gcm.New(client) + if err != nil { + return nil, err + } + + sink := &gcmSink{ + registered: false, + project: projectId, + gcmService: gcmService, + metricFilter: metricFilter, + } + glog.Infof("created GCM sink") + if err := sink.registerAllMetrics(); err != nil { + glog.Warningf("Error during metrics registration: %v", err) + } + return sink, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/hawkular/client.go b/vendor/k8s.io/heapster/metrics/sinks/hawkular/client.go new file mode 100644 index 0000000000..076cfdf85d --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/hawkular/client.go @@ -0,0 +1,340 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hawkular + +import ( + "fmt" + "math" + "regexp" + "strings" + "sync" + "time" + + "github.com/golang/glog" + "github.com/hawkular/hawkular-client-go/metrics" + "k8s.io/heapster/metrics/core" +) + +// Fetches definitions from the server and checks that they're matching the descriptors +func (h *hawkularSink) updateDefinitions(mt metrics.MetricType) error { + m := make([]metrics.Modifier, len(h.modifiers), len(h.modifiers)+1) + copy(m, h.modifiers) + m = append(m, metrics.Filters(metrics.TypeFilter(mt))) + + mds, err := h.client.Definitions(m...) + if err != nil { + return err + } + + h.regLock.Lock() + defer h.regLock.Unlock() + + for _, p := range mds { + // If no descriptorTag is found, this metric does not belong to Heapster + if mk, found := p.Tags[descriptorTag]; found { + if model, f := h.models[mk]; f && !h.recent(p, model) { + if err := h.client.UpdateTags(mt, p.Id, p.Tags, h.modifiers...); err != nil { + return err + } + } + h.reg[p.Id] = p + } + } + return nil +} + +// Checks that stored definition is up to date with the model +func (h *hawkularSink) recent(live *metrics.MetricDefinition, model *metrics.MetricDefinition) bool { + recent := true + for k := range model.Tags { + if v, found := live.Tags[k]; !found { + // There's a label that wasn't in our stored definition + live.Tags[k] = v + recent = false + } + } + + return recent +} + +// Transform the MetricDescriptor to a format used by Hawkular-Metrics +func (h *hawkularSink) descriptorToDefinition(md *core.MetricDescriptor) metrics.MetricDefinition { + tags := make(map[string]string) + // Postfix description tags with _description + for _, l := range md.Labels { + if len(l.Description) > 0 { + tags[l.Key+descriptionTag] = l.Description + } + } + + if len(md.Units.String()) > 0 { + tags[unitsTag] = md.Units.String() + } + + tags[descriptorTag] = md.Name + + hmd := metrics.MetricDefinition{ + Id: md.Name, + Tags: tags, + Type: heapsterTypeToHawkularType(md.Type), + } + + return hmd +} + +func (h *hawkularSink) groupName(ms *core.MetricSet, metricName string) string { + n := []string{ms.Labels[core.LabelContainerName.Key], metricName} + return strings.Join(n, separator) +} + +func (h *hawkularSink) idName(ms *core.MetricSet, metricName string) string { + n := make([]string, 0, 3) + + metricType := ms.Labels[core.LabelMetricSetType.Key] + switch metricType { + case core.MetricSetTypeNode: + n = append(n, "machine") + n = append(n, h.nodeName(ms)) + case core.MetricSetTypeSystemContainer: + n = append(n, core.MetricSetTypeSystemContainer) + n = append(n, ms.Labels[core.LabelContainerName.Key]) + n = append(n, ms.Labels[core.LabelPodId.Key]) + case core.MetricSetTypeCluster: + n = append(n, core.MetricSetTypeCluster) + case core.MetricSetTypeNamespace: + n = append(n, core.MetricSetTypeNamespace) + n = append(n, ms.Labels[core.LabelNamespaceName.Key]) + case core.MetricSetTypePod: + n = append(n, core.MetricSetTypePod) + n = append(n, ms.Labels[core.LabelPodId.Key]) + case core.MetricSetTypePodContainer: + n = append(n, ms.Labels[core.LabelContainerName.Key]) + n = append(n, ms.Labels[core.LabelPodId.Key]) + default: + n = append(n, ms.Labels[core.LabelContainerName.Key]) + if ms.Labels[core.LabelPodId.Key] != "" { + n = append(n, ms.Labels[core.LabelPodId.Key]) + } else { + n = append(n, h.nodeName(ms)) + } + } + + n = append(n, metricName) + + return strings.Join(n, separator) +} + +func (h *hawkularSink) nodeName(ms *core.MetricSet) string { + if len(h.labelNodeId) > 0 { + if v, found := ms.Labels[h.labelNodeId]; found { + return v + } + glog.V(4).Infof("The labelNodeId was set to %s but there is no label with this value."+ + "Using the default 'nodename' label instead.", h.labelNodeId) + } + + return ms.Labels[core.LabelNodename.Key] +} + +func (h *hawkularSink) registerLabeledIfNecessary(ms *core.MetricSet, metric core.LabeledMetric, m ...metrics.Modifier) error { + key := h.idName(ms, metric.Name) + + if resourceID, found := metric.Labels[core.LabelResourceID.Key]; found { + key = h.idName(ms, metric.Name+separator+resourceID) + } + + h.regLock.Lock() + defer h.regLock.Unlock() + + // If found, check it matches the current stored definition (could be old info from + // the stored metrics cache for example) + if _, found := h.reg[key]; !found { + // Register the metric descriptor here.. + if md, f := h.models[metric.Name]; f { + // Copy the original map + mdd := *md + tags := make(map[string]string) + for k, v := range mdd.Tags { + tags[k] = v + } + mdd.Tags = tags + + // Set tag values + for k, v := range ms.Labels { + mdd.Tags[k] = v + } + + // Set the labelled values + for k, v := range metric.Labels { + mdd.Tags[k] = v + } + + mdd.Tags[groupTag] = h.groupName(ms, metric.Name) + mdd.Tags[descriptorTag] = metric.Name + + m = append(m, h.modifiers...) + + // Create metric, use updateTags instead of Create because we know it is unique + if err := h.client.UpdateTags(heapsterTypeToHawkularType(metric.MetricType), key, mdd.Tags, m...); err != nil { + // Log error and don't add this key to the lookup table + glog.Errorf("Could not update tags: %s", err) + return err + } + + // Add to the lookup table + h.reg[key] = &mdd + } else { + return fmt.Errorf("Could not find definition model with name %s", metric.Name) + } + } + // TODO Compare the definition tags and update if necessary? Quite expensive operation.. + + return nil +} + +func toBatches(m []metrics.MetricHeader, batchSize int) chan []metrics.MetricHeader { + if batchSize == 0 { + c := make(chan []metrics.MetricHeader, 1) + c <- m + return c + } + + size := int(math.Ceil(float64(len(m)) / float64(batchSize))) + c := make(chan []metrics.MetricHeader, size) + + for i := 0; i < len(m); i += batchSize { + n := i + batchSize + if len(m) < n { + n = len(m) + } + part := m[i:n] + c <- part + } + + return c +} + +func (h *hawkularSink) sendData(tmhs map[string][]metrics.MetricHeader, wg *sync.WaitGroup) { + for k, v := range tmhs { + parts := toBatches(v, h.batchSize) + close(parts) + + for p := range parts { + wg.Add(1) + go func(batch []metrics.MetricHeader, tenant string) { + defer wg.Done() + + m := make([]metrics.Modifier, len(h.modifiers), len(h.modifiers)+1) + copy(m, h.modifiers) + m = append(m, metrics.Tenant(tenant)) + if err := h.client.Write(batch, m...); err != nil { + glog.Errorf(err.Error()) + } + }(p, k) + } + } +} + +// Converts Timeseries to metric structure used by the Hawkular +func (h *hawkularSink) pointToLabeledMetricHeader(ms *core.MetricSet, metric core.LabeledMetric, timestamp time.Time) (*metrics.MetricHeader, error) { + + name := h.idName(ms, metric.Name) + if resourceID, found := metric.Labels[core.LabelResourceID.Key]; found { + name = h.idName(ms, metric.Name+separator+resourceID) + } + + var value float64 + if metric.ValueType == core.ValueInt64 { + value = float64(metric.IntValue) + } else { + value = float64(metric.FloatValue) + } + + m := metrics.Datapoint{ + Value: value, + Timestamp: metrics.UnixMilli(timestamp), + } + + mh := &metrics.MetricHeader{ + Id: name, + Data: []metrics.Datapoint{m}, + Type: heapsterTypeToHawkularType(metric.MetricType), + } + + return mh, nil +} + +// If Heapster gets filters, remove these.. +func parseFilters(v []string) ([]Filter, error) { + fs := make([]Filter, 0, len(v)) + for _, s := range v { + p := strings.Index(s, "(") + if p < 0 { + return nil, fmt.Errorf("Incorrect syntax in filter parameters, missing (") + } + + if strings.Index(s, ")") != len(s)-1 { + return nil, fmt.Errorf("Incorrect syntax in filter parameters, missing )") + } + + t := Unknown.From(s[:p]) + if t == Unknown { + return nil, fmt.Errorf("Unknown filter type") + } + + command := s[p+1 : len(s)-1] + + switch t { + case Label: + proto := strings.SplitN(command, ":", 2) + if len(proto) < 2 { + return nil, fmt.Errorf("Missing : from label filter") + } + r, err := regexp.Compile(proto[1]) + if err != nil { + return nil, err + } + fs = append(fs, labelFilter(proto[0], r)) + break + case Name: + r, err := regexp.Compile(command) + if err != nil { + return nil, err + } + fs = append(fs, nameFilter(r)) + break + } + } + return fs, nil +} + +func labelFilter(label string, r *regexp.Regexp) Filter { + return func(ms *core.MetricSet, metricName string) bool { + for k, v := range ms.Labels { + if k == label { + if r.MatchString(v) { + return false + } + } + } + return true + } +} + +func nameFilter(r *regexp.Regexp) Filter { + return func(ms *core.MetricSet, metricName string) bool { + return !r.MatchString(metricName) + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/hawkular/driver.go b/vendor/k8s.io/heapster/metrics/sinks/hawkular/driver.go new file mode 100644 index 0000000000..de583590d9 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/hawkular/driver.go @@ -0,0 +1,324 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hawkular + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "sync" + + "github.com/golang/glog" + "github.com/hawkular/hawkular-client-go/metrics" + + "k8s.io/heapster/metrics/core" + kube_client "k8s.io/kubernetes/pkg/client/restclient" + kubeClientCmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" +) + +const ( + unitsTag = "units" + descriptionTag = "_description" + descriptorTag = "descriptor_name" + groupTag = "group_id" + separator = "/" + batchSizeDefault = 1000 + concurrencyDefault = 5 + + nodeId string = "labelNodeId" + + defaultServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" +) + +// START: ExternalSink interface implementations + +func (h *hawkularSink) Register(mds []core.MetricDescriptor) error { + // Create model definitions based on the MetricDescriptors + for _, md := range mds { + hmd := h.descriptorToDefinition(&md) + h.models[md.Name] = &hmd + } + + // Fetch currently known metrics from Hawkular-Metrics and cache them + types := []metrics.MetricType{metrics.Gauge, metrics.Counter} + for _, t := range types { + err := h.updateDefinitions(t) + if err != nil { + return err + } + } + + return nil +} + +func (h *hawkularSink) Stop() { + h.regLock.Lock() + defer h.regLock.Unlock() + h.init() +} + +func (h *hawkularSink) ExportData(db *core.DataBatch) { + totalCount := 0 + for _, ms := range db.MetricSets { + totalCount += len(ms.MetricValues) + totalCount += len(ms.LabeledMetrics) + } + + if len(db.MetricSets) > 0 { + tmhs := make(map[string][]metrics.MetricHeader) + + if len(h.labelTenant) == 0 { + tmhs[h.client.Tenant] = make([]metrics.MetricHeader, 0, totalCount) + } + + wg := &sync.WaitGroup{} + + for _, ms := range db.MetricSets { + + // // Transform ms.MetricValues to LabeledMetrics first + lms := metricValueToLabeledMetric(ms.MetricValues) + ms.LabeledMetrics = append(ms.LabeledMetrics, lms...) + + Store: + for _, labeledMetric := range ms.LabeledMetrics { + + for _, filter := range h.filters { + if !filter(ms, labeledMetric.Name) { + continue Store + } + } + + tenant := h.client.Tenant + + if len(h.labelTenant) > 0 { + if v, found := ms.Labels[h.labelTenant]; found { + tenant = v + } + } + + wg.Add(1) + go func(ms *core.MetricSet, labeledMetric core.LabeledMetric, tenant string) { + defer wg.Done() + h.registerLabeledIfNecessary(ms, labeledMetric, metrics.Tenant(tenant)) + }(ms, labeledMetric, tenant) + + mH, err := h.pointToLabeledMetricHeader(ms, labeledMetric, db.Timestamp) + if err != nil { + // One transformation error should not prevent the whole process + glog.Errorf(err.Error()) + continue + } + + if _, found := tmhs[tenant]; !found { + tmhs[tenant] = make([]metrics.MetricHeader, 0) + } + + tmhs[tenant] = append(tmhs[tenant], *mH) + } + } + h.sendData(tmhs, wg) // Send to a limited channel? Only batches.. egg. + wg.Wait() + } +} + +func metricValueToLabeledMetric(msValues map[string]core.MetricValue) []core.LabeledMetric { + lms := make([]core.LabeledMetric, 0, len(msValues)) + for metricName, metricValue := range msValues { + lm := core.LabeledMetric{ + Name: metricName, + MetricValue: metricValue, + Labels: make(map[string]string, 0), + } + lms = append(lms, lm) + } + return lms +} + +func (h *hawkularSink) DebugInfo() string { + info := fmt.Sprintf("%s\n", h.Name()) + + h.regLock.Lock() + defer h.regLock.Unlock() + info += fmt.Sprintf("Known metrics: %d\n", len(h.reg)) + if len(h.labelTenant) > 0 { + info += fmt.Sprintf("Using label '%s' as tenant information\n", h.labelTenant) + } + if len(h.labelNodeId) > 0 { + info += fmt.Sprintf("Using label '%s' as node identified in resourceid\n", h.labelNodeId) + } + + // TODO Add here statistics from the Hawkular-Metrics client instance + return info +} + +func (h *hawkularSink) Name() string { + return "Hawkular-Metrics Sink" +} + +// NewHawkularSink Creates and returns a new hawkularSink instance +func NewHawkularSink(u *url.URL) (core.DataSink, error) { + sink := &hawkularSink{ + uri: u, + batchSize: 1000, + } + if err := sink.init(); err != nil { + return nil, err + } + + metrics := make([]core.MetricDescriptor, 0, len(core.AllMetrics)) + for _, metric := range core.AllMetrics { + metrics = append(metrics, metric.MetricDescriptor) + } + sink.Register(metrics) + return sink, nil +} + +func (h *hawkularSink) init() error { + h.reg = make(map[string]*metrics.MetricDefinition) + h.models = make(map[string]*metrics.MetricDefinition) + h.modifiers = make([]metrics.Modifier, 0) + h.filters = make([]Filter, 0) + h.batchSize = batchSizeDefault + + p := metrics.Parameters{ + Tenant: "heapster", + Url: h.uri.String(), + Concurrency: concurrencyDefault, + } + + opts := h.uri.Query() + + if v, found := opts["tenant"]; found { + p.Tenant = v[0] + } + + if v, found := opts["labelToTenant"]; found { + h.labelTenant = v[0] + } + + if v, found := opts[nodeId]; found { + h.labelNodeId = v[0] + } + + if v, found := opts["useServiceAccount"]; found { + if b, _ := strconv.ParseBool(v[0]); b { + // If a readable service account token exists, then use it + if contents, err := ioutil.ReadFile(defaultServiceAccountFile); err == nil { + p.Token = string(contents) + } + } + } + + // Authentication / Authorization parameters + tC := &tls.Config{} + + if v, found := opts["auth"]; found { + if _, f := opts["caCert"]; f { + return fmt.Errorf("Both auth and caCert files provided, combination is not supported") + } + if len(v[0]) > 0 { + // Authfile + kubeConfig, err := kubeClientCmd.NewNonInteractiveDeferredLoadingClientConfig(&kubeClientCmd.ClientConfigLoadingRules{ + ExplicitPath: v[0]}, + &kubeClientCmd.ConfigOverrides{}).ClientConfig() + if err != nil { + return err + } + tC, err = kube_client.TLSConfigFor(kubeConfig) + if err != nil { + return err + } + } + } + + if u, found := opts["user"]; found { + if _, wrong := opts["useServiceAccount"]; wrong { + return fmt.Errorf("If user and password are used, serviceAccount cannot be used") + } + if p, f := opts["pass"]; f { + h.modifiers = append(h.modifiers, func(req *http.Request) error { + req.SetBasicAuth(u[0], p[0]) + return nil + }) + } + } + + if v, found := opts["caCert"]; found { + caCert, err := ioutil.ReadFile(v[0]) + if err != nil { + return err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + tC.RootCAs = caCertPool + } + + if v, found := opts["insecure"]; found { + _, f := opts["caCert"] + _, f2 := opts["auth"] + if f || f2 { + return fmt.Errorf("Insecure can't be defined with auth or caCert") + } + insecure, err := strconv.ParseBool(v[0]) + if err != nil { + return err + } + tC.InsecureSkipVerify = insecure + } + + p.TLSConfig = tC + + // Filters + if v, found := opts["filter"]; found { + filters, err := parseFilters(v) + if err != nil { + return err + } + h.filters = filters + } + + // Concurrency limitations + if v, found := opts["concurrencyLimit"]; found { + cs, err := strconv.Atoi(v[0]) + if err != nil || cs < 0 { + return fmt.Errorf("Supplied concurrency value of %s is invalid", v[0]) + } + p.Concurrency = cs + } + + if v, found := opts["batchSize"]; found { + bs, err := strconv.Atoi(v[0]) + if err != nil || bs < 0 { + return fmt.Errorf("Supplied batchSize value of %s is invalid", v[0]) + } + h.batchSize = bs + } + + c, err := metrics.NewHawkularClient(p) + if err != nil { + return err + } + + h.client = c + + glog.Infof("Initialised Hawkular Sink with parameters %v", p) + return nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/hawkular/driver_test.go b/vendor/k8s.io/heapster/metrics/sinks/hawkular/driver_test.go new file mode 100644 index 0000000000..9e195bbafe --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/hawkular/driver_test.go @@ -0,0 +1,673 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hawkular + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "sync" + "testing" + "time" + + "github.com/hawkular/hawkular-client-go/metrics" + "k8s.io/heapster/metrics/core" + + assert "github.com/stretchr/testify/require" +) + +func dummySink() *hawkularSink { + return &hawkularSink{ + reg: make(map[string]*metrics.MetricDefinition), + models: make(map[string]*metrics.MetricDefinition), + } +} + +func TestDescriptorTransform(t *testing.T) { + + hSink := dummySink() + + ld := core.LabelDescriptor{ + Key: "k1", + Description: "d1", + } + smd := core.MetricDescriptor{ + Name: "test/metric/1", + Units: core.UnitsBytes, + ValueType: core.ValueInt64, + Type: core.MetricGauge, + Labels: []core.LabelDescriptor{ld}, + } + + md := hSink.descriptorToDefinition(&smd) + + assert.Equal(t, smd.Name, md.Id) + assert.Equal(t, 3, len(md.Tags)) // descriptorTag, unitsTag, typesTag, k1 + + assert.Equal(t, smd.Units.String(), md.Tags[unitsTag]) + assert.Equal(t, "d1", md.Tags["k1_description"]) + + smd.Type = core.MetricCumulative + + md = hSink.descriptorToDefinition(&smd) + assert.Equal(t, md.Type, metrics.Counter) +} + +func TestMetricTransform(t *testing.T) { + hSink := dummySink() + + l := make(map[string]string) + l["spooky"] = "notvisible" + l[core.LabelHostname.Key] = "localhost" + l[core.LabelHostID.Key] = "localhost" + l[core.LabelContainerName.Key] = "docker" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + l[core.LabelNodename.Key] = "myNode" + + metricName := "test/metric/1" + labeledMetricNameA := "test/labeledmetric/A" + labeledMetricNameB := "test/labeledmetric/B" + + metricSet := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + metricName: { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 123456, + }, + }, + LabeledMetrics: []core.LabeledMetric{ + { + Name: labeledMetricNameA, + Labels: map[string]string{ + core.LabelResourceID.Key: "XYZ", + }, + MetricValue: core.MetricValue{ + MetricType: core.MetricGauge, + FloatValue: 124.456, + }, + }, + { + Name: labeledMetricNameB, + MetricValue: core.MetricValue{ + MetricType: core.MetricGauge, + FloatValue: 454, + }, + }, + }, + } + + metricSet.LabeledMetrics = append(metricSet.LabeledMetrics, metricValueToLabeledMetric(metricSet.MetricValues)...) + + now := time.Now() + // + m, err := hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[2], now) + assert.NoError(t, err) + + assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], + metricSet.Labels[core.LabelPodId.Key], metricName), m.Id) + + assert.Equal(t, 1, len(m.Data)) + _, ok := m.Data[0].Value.(float64) + assert.True(t, ok, "Value should have been converted to float64") + + delete(l, core.LabelPodId.Key) + + // + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[2], now) + assert.NoError(t, err) + + assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], metricSet.Labels[core.LabelNodename.Key], metricName), m.Id) + + // + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + + assert.Equal(t, fmt.Sprintf("%s/%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], + metricSet.Labels[core.LabelNodename.Key], labeledMetricNameA, + metricSet.LabeledMetrics[0].Labels[core.LabelResourceID.Key]), m.Id) + + // + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[1], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], + metricSet.Labels[core.LabelNodename.Key], labeledMetricNameB), m.Id) +} + +func TestMetricIds(t *testing.T) { + hSink := dummySink() + + l := make(map[string]string) + l["spooky"] = "notvisible" + l[core.LabelHostname.Key] = "localhost" + l[core.LabelHostID.Key] = "localhost" + l[core.LabelContainerName.Key] = "docker" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + l[core.LabelNodename.Key] = "myNode" + l[core.LabelNamespaceName.Key] = "myNamespace" + + metricName := "test/metric/nodeType" + + metricSet := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + metricName: { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 123456, + }, + }, + } + metricSet.LabeledMetrics = metricValueToLabeledMetric(metricSet.MetricValues) + + now := time.Now() + // + m, err := hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], metricSet.Labels[core.LabelPodId.Key], metricName), m.Id) + + // + metricSet.Labels[core.LabelMetricSetType.Key] = core.MetricSetTypeNode + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s", "machine", metricSet.Labels[core.LabelNodename.Key], metricName), m.Id) + + // + metricSet.Labels[core.LabelMetricSetType.Key] = core.MetricSetTypePod + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s", core.MetricSetTypePod, metricSet.Labels[core.LabelPodId.Key], metricName), m.Id) + + // + metricSet.Labels[core.LabelMetricSetType.Key] = core.MetricSetTypePodContainer + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s", metricSet.Labels[core.LabelContainerName.Key], metricSet.Labels[core.LabelPodId.Key], metricName), m.Id) + + // + metricSet.Labels[core.LabelMetricSetType.Key] = core.MetricSetTypeSystemContainer + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s/%s", core.MetricSetTypeSystemContainer, metricSet.Labels[core.LabelContainerName.Key], metricSet.Labels[core.LabelPodId.Key], metricName), m.Id) + + // + metricSet.Labels[core.LabelMetricSetType.Key] = core.MetricSetTypeCluster + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s", core.MetricSetTypeCluster, metricName), m.Id) + + // + metricSet.Labels[core.LabelMetricSetType.Key] = core.MetricSetTypeNamespace + m, err = hSink.pointToLabeledMetricHeader(&metricSet, metricSet.LabeledMetrics[0], now) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("%s/%s/%s", core.MetricSetTypeNamespace, metricSet.Labels[core.LabelNamespaceName.Key], metricName), m.Id) + +} + +func TestRecentTest(t *testing.T) { + hSink := dummySink() + + modelT := make(map[string]string) + + id := "test.name" + modelT[descriptorTag] = "d" + modelT[groupTag] = id + modelT["hep"+descriptionTag] = "n" + + model := metrics.MetricDefinition{ + Id: id, + Tags: modelT, + } + + liveT := make(map[string]string) + for k, v := range modelT { + liveT[k] = v + } + + live := metrics.MetricDefinition{ + Id: "test/" + id, + Tags: liveT, + } + + assert.True(t, hSink.recent(&live, &model), "Tags are equal, live is newest") + + delete(liveT, "hep"+descriptionTag) + live.Tags = liveT + + assert.False(t, hSink.recent(&live, &model), "Tags are not equal, live isn't recent") + +} + +func TestParseFiltersErrors(t *testing.T) { + _, err := parseFilters([]string{"(missingcommand)"}) + assert.Error(t, err) + + _, err = parseFilters([]string{"missingeverything"}) + assert.Error(t, err) + + _, err = parseFilters([]string{"labelstart:^missing$)"}) + assert.Error(t, err) + + _, err = parseFilters([]string{"label(endmissing"}) + assert.Error(t, err) + + _, err = parseFilters([]string{"label(wrongsyntax)"}) + assert.Error(t, err) +} + +// Integration tests +func integSink(uri string) (*hawkularSink, error) { + + u, err := url.Parse(uri) + if err != nil { + return nil, err + } + + sink := &hawkularSink{ + uri: u, + } + if err = sink.init(); err != nil { + return nil, err + } + + return sink, nil +} + +// Test that Definitions is called for Gauges & Counters +// Test that we have single registered model +// Test that the tags for metric is updated.. +func TestRegister(t *testing.T) { + m := &sync.Mutex{} + definitionsCalled := make(map[string]bool) + updateTagsCalled := false + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + m.Lock() + defer m.Unlock() + w.Header().Set("Content-Type", "application/json") + + if strings.Contains(r.RequestURI, "metrics?type=") { + typ := r.RequestURI[strings.Index(r.RequestURI, "type=")+5:] + definitionsCalled[typ] = true + if typ == "gauge" { + fmt.Fprintln(w, `[{ "id": "test.create.gauge.1", "tenantId": "test-heapster", "type": "gauge", "tags": { "descriptor_name": "test/metric/1" } }]`) + } else { + w.WriteHeader(http.StatusNoContent) + } + } else if strings.Contains(r.RequestURI, "/tags") && r.Method == "PUT" { + updateTagsCalled = true + // assert.True(t, strings.Contains(r.RequestURI, "k1:d1"), "Tag k1 was not updated with value d1") + defer r.Body.Close() + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + tags := make(map[string]string) + err = json.Unmarshal(b, &tags) + assert.NoError(t, err) + + _, kt1 := tags["k1_description"] + _, dt := tags["descriptor_name"] + + assert.True(t, kt1, "k1_description tag is missing") + assert.True(t, dt, "descriptor_name is missing") + + w.WriteHeader(http.StatusOK) + } + })) + defer s.Close() + + hSink, err := integSink(s.URL + "?tenant=test-heapster") + assert.NoError(t, err) + + md := make([]core.MetricDescriptor, 0, 1) + ld := core.LabelDescriptor{ + Key: "k1", + Description: "d1", + } + smd := core.MetricDescriptor{ + Name: "test/metric/1", + Units: core.UnitsBytes, + ValueType: core.ValueInt64, + Type: core.MetricGauge, + Labels: []core.LabelDescriptor{ld}, + } + smdg := core.MetricDescriptor{ + Name: "test/metric/2", + Units: core.UnitsBytes, + ValueType: core.ValueFloat, + Type: core.MetricCumulative, + Labels: []core.LabelDescriptor{}, + } + + md = append(md, smd, smdg) + + err = hSink.Register(md) + assert.NoError(t, err) + + assert.Equal(t, 2, len(hSink.models)) + assert.Equal(t, 1, len(hSink.reg)) + + assert.True(t, definitionsCalled["gauge"], "Gauge definitions were not fetched") + assert.True(t, definitionsCalled["counter"], "Counter definitions were not fetched") + assert.True(t, updateTagsCalled, "Updating outdated tags was not called") +} + +// Store timeseries with both gauges and cumulatives +func TestStoreTimeseries(t *testing.T) { + m := &sync.Mutex{} + ids := make([]string, 0, 2) + calls := make([]string, 0, 2) + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + m.Lock() + defer m.Unlock() + calls = append(calls, r.RequestURI) + w.Header().Set("Content-Type", "application/json") + + typ := r.RequestURI[strings.Index(r.RequestURI, "hawkular/metrics/")+17:] + typ = typ[:len(typ)-5] + + switch typ { + case "counters": + assert.Equal(t, "test-label", r.Header.Get("Hawkular-Tenant")) + break + case "gauges": + assert.Equal(t, "test-heapster", r.Header.Get("Hawkular-Tenant")) + break + default: + assert.FailNow(t, "Unrecognized type "+typ) + } + + defer r.Body.Close() + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + mH := []metrics.MetricHeader{} + err = json.Unmarshal(b, &mH) + assert.NoError(t, err) + + assert.Equal(t, 1, len(mH)) + + ids = append(ids, mH[0].Id) + })) + defer s.Close() + + hSink, err := integSink(s.URL + "?tenant=test-heapster&labelToTenant=projectId") + assert.NoError(t, err) + + l := make(map[string]string) + l["projectId"] = "test-label" + l[core.LabelContainerName.Key] = "test-container" + l[core.LabelPodId.Key] = "test-podid" + + lg := make(map[string]string) + lg[core.LabelContainerName.Key] = "test-container" + lg[core.LabelPodId.Key] = "test-podid" + + metricSet1 := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet2 := core.MetricSet{ + Labels: lg, + MetricValues: map[string]core.MetricValue{ + "test/metric/2": { + ValueType: core.ValueFloat, + MetricType: core.MetricGauge, + FloatValue: 123.456, + }, + }, + } + + data := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet1, + "pod2": &metricSet2, + }, + } + + hSink.ExportData(&data) + assert.Equal(t, 2, len(calls)) + assert.Equal(t, 2, len(ids)) + + assert.NotEqual(t, ids[0], ids[1]) +} + +func TestUserPass(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Authorization", r.Header.Get("Authorization")) + auth := strings.SplitN(r.Header.Get("Authorization"), " ", 2) + if len(auth) != 2 || auth[0] != "Basic" { + assert.FailNow(t, "Could not find Basic authentication") + } + assert.True(t, len(auth[1]) > 0) + w.WriteHeader(http.StatusNoContent) + })) + defer s.Close() + + hSink, err := integSink(s.URL + "?user=tester&pass=hidden") + assert.NoError(t, err) + + // md := make([]core.MetricDescriptor, 0, 1) + ld := core.LabelDescriptor{ + Key: "k1", + Description: "d1", + } + smd := core.MetricDescriptor{ + Name: "test/metric/1", + Units: core.UnitsBytes, + ValueType: core.ValueInt64, + Type: core.MetricGauge, + Labels: []core.LabelDescriptor{ld}, + } + err = hSink.Register([]core.MetricDescriptor{smd}) + assert.NoError(t, err) +} + +func TestFiltering(t *testing.T) { + m := &sync.Mutex{} + mH := []metrics.MetricHeader{} + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + m.Lock() + defer m.Unlock() + if strings.Contains(r.RequestURI, "data") { + defer r.Body.Close() + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + err = json.Unmarshal(b, &mH) + assert.NoError(t, err) + } + })) + defer s.Close() + + hSink, err := integSink(s.URL + "?filter=label(namespace_id:^$)&filter=label(container_name:^[/system.slice/|/user.slice].*)&filter=name(remove*)") + assert.NoError(t, err) + + l := make(map[string]string) + l["namespace_id"] = "123" + l["container_name"] = "/system.slice/-.mount" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l2 := make(map[string]string) + l2["namespace_id"] = "123" + l2["container_name"] = "/system.slice/dbus.service" + l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l3 := make(map[string]string) + l3["namespace_id"] = "123" + l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l4 := make(map[string]string) + l4["namespace_id"] = "" + l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l5 := make(map[string]string) + l5["namespace_id"] = "123" + l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + metricSet1 := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + "/system.slice/-.mount//cpu/limit": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet2 := core.MetricSet{ + Labels: l2, + MetricValues: map[string]core.MetricValue{ + "/system.slice/dbus.service//cpu/usage": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet3 := core.MetricSet{ + Labels: l3, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet4 := core.MetricSet{ + Labels: l4, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet5 := core.MetricSet{ + Labels: l5, + MetricValues: map[string]core.MetricValue{ + "removeme": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + data := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet1, + "pod2": &metricSet2, + "pod3": &metricSet3, + "pod4": &metricSet4, + "pod5": &metricSet5, + }, + } + hSink.ExportData(&data) + + assert.Equal(t, 1, len(mH)) +} + +func TestBatchingTimeseries(t *testing.T) { + total := 1000 + m := &sync.Mutex{} + ids := make([]string, 0, total) + calls := 0 + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + m.Lock() + defer m.Unlock() + + w.Header().Set("Content-Type", "application/json") + + defer r.Body.Close() + b, err := ioutil.ReadAll(r.Body) + assert.NoError(t, err) + + mH := []metrics.MetricHeader{} + err = json.Unmarshal(b, &mH) + assert.NoError(t, err) + + for _, v := range mH { + ids = append(ids, v.Id) + } + + calls++ + })) + defer s.Close() + + hSink, err := integSink(s.URL + "?tenant=test-heapster&labelToTenant=projectId&batchSize=20&concurrencyLimit=5") + assert.NoError(t, err) + + l := make(map[string]string) + l["projectId"] = "test-label" + l[core.LabelContainerName.Key] = "test-container" + l[core.LabelPodId.Key] = "test-podid" + + metrics := make(map[string]core.MetricValue) + for i := 0; i < total; i++ { + id := fmt.Sprintf("test/metric/%d", i) + metrics[id] = core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123 * int64(i), + } + } + + metricSet := core.MetricSet{ + Labels: l, + MetricValues: metrics, + } + + data := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet, + }, + } + + hSink.ExportData(&data) + assert.Equal(t, total, len(ids)) + assert.Equal(t, calls, 50) + + // Verify that all ids are unique + newIds := make(map[string]bool) + for _, v := range ids { + if newIds[v] { + t.Errorf("Key %s was duplicate", v) + } + newIds[v] = true + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/hawkular/types.go b/vendor/k8s.io/heapster/metrics/sinks/hawkular/types.go new file mode 100644 index 0000000000..b8afeaf458 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/hawkular/types.go @@ -0,0 +1,73 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hawkular + +import ( + "net/url" + "sync" + + "github.com/hawkular/hawkular-client-go/metrics" + "k8s.io/heapster/metrics/core" +) + +type Filter func(ms *core.MetricSet, metricName string) bool +type FilterType int + +const ( + // Filter by label's value + Label FilterType = iota + // Filter by metric name + Name + // Unknown filter type + Unknown +) + +func (f FilterType) From(s string) FilterType { + switch s { + case "label": + return Label + case "name": + return Name + default: + return Unknown + } +} + +type hawkularSink struct { + client *metrics.Client + models map[string]*metrics.MetricDefinition // Model definitions + regLock sync.Mutex + reg map[string]*metrics.MetricDefinition // Real definitions + + uri *url.URL + + labelTenant string + labelNodeId string + modifiers []metrics.Modifier + filters []Filter + + batchSize int +} + +func heapsterTypeToHawkularType(t core.MetricType) metrics.MetricType { + switch t { + case core.MetricCumulative: + return metrics.Counter + case core.MetricGauge: + return metrics.Gauge + default: + return metrics.Gauge + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb.go b/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb.go new file mode 100644 index 0000000000..2d4ffbda42 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb.go @@ -0,0 +1,233 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "fmt" + "net/url" + "strings" + "sync" + "time" + + influxdb_common "k8s.io/heapster/common/influxdb" + "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" + influxdb "github.com/influxdata/influxdb/client" +) + +type influxdbSink struct { + client influxdb_common.InfluxdbClient + sync.RWMutex + c influxdb_common.InfluxdbConfig + dbExists bool +} + +const ( + // Value Field name + valueField = "value" + // Event special tags + dbNotFoundError = "database not found" + + // Maximum number of influxdb Points to be sent in one batch. + maxSendBatchSize = 10000 +) + +func (sink *influxdbSink) resetConnection() { + glog.Infof("Influxdb connection reset") + sink.dbExists = false + sink.client = nil +} + +func (sink *influxdbSink) ExportData(dataBatch *core.DataBatch) { + sink.Lock() + defer sink.Unlock() + + dataPoints := make([]influxdb.Point, 0, 0) + for _, metricSet := range dataBatch.MetricSets { + for metricName, metricValue := range metricSet.MetricValues { + + var value interface{} + if core.ValueInt64 == metricValue.ValueType { + value = metricValue.IntValue + } else if core.ValueFloat == metricValue.ValueType { + value = float64(metricValue.FloatValue) + } else { + continue + } + + // Prepare measurement without fields + fieldName := "value" + measurementName := metricName + if sink.c.WithFields { + // Prepare measurement and field names + serieName := strings.SplitN(metricName, "/", 2) + measurementName = serieName[0] + if len(serieName) > 1 { + fieldName = serieName[1] + } + } + + point := influxdb.Point{ + Measurement: measurementName, + Tags: metricSet.Labels, + Fields: map[string]interface{}{ + fieldName: value, + }, + Time: dataBatch.Timestamp.UTC(), + } + dataPoints = append(dataPoints, point) + if len(dataPoints) >= maxSendBatchSize { + sink.sendData(dataPoints) + dataPoints = make([]influxdb.Point, 0, 0) + } + } + + for _, labeledMetric := range metricSet.LabeledMetrics { + + var value interface{} + if core.ValueInt64 == labeledMetric.ValueType { + value = labeledMetric.IntValue + } else if core.ValueFloat == labeledMetric.ValueType { + value = float64(labeledMetric.FloatValue) + } else { + continue + } + + // Prepare measurement without fields + fieldName := "value" + measurementName := labeledMetric.Name + if sink.c.WithFields { + // Prepare measurement and field names + serieName := strings.SplitN(labeledMetric.Name, "/", 2) + measurementName = serieName[0] + if len(serieName) > 1 { + fieldName = serieName[1] + } + } + + point := influxdb.Point{ + Measurement: measurementName, + Tags: make(map[string]string), + Fields: map[string]interface{}{ + fieldName: value, + }, + Time: dataBatch.Timestamp.UTC(), + } + for key, value := range metricSet.Labels { + point.Tags[key] = value + } + for key, value := range labeledMetric.Labels { + point.Tags[key] = value + } + + dataPoints = append(dataPoints, point) + if len(dataPoints) >= maxSendBatchSize { + sink.sendData(dataPoints) + dataPoints = make([]influxdb.Point, 0, 0) + } + } + } + if len(dataPoints) >= 0 { + sink.sendData(dataPoints) + } +} + +func (sink *influxdbSink) sendData(dataPoints []influxdb.Point) { + if err := sink.createDatabase(); err != nil { + glog.Errorf("Failed to create infuxdb: %v", err) + return + } + bp := influxdb.BatchPoints{ + Points: dataPoints, + Database: sink.c.DbName, + RetentionPolicy: "default", + } + + start := time.Now() + if _, err := sink.client.Write(bp); err != nil { + if strings.Contains(err.Error(), dbNotFoundError) { + sink.resetConnection() + } else if _, _, err := sink.client.Ping(); err != nil { + glog.Errorf("InfluxDB ping failed: %v", err) + sink.resetConnection() + } + } + end := time.Now() + glog.V(4).Infof("Exported %d data to influxDB in %s", len(dataPoints), end.Sub(start)) +} + +func (sink *influxdbSink) Name() string { + return "InfluxDB Sink" +} + +func (sink *influxdbSink) Stop() { + // nothing needs to be done. +} + +func (sink *influxdbSink) ensureClient() error { + if sink.client == nil { + client, err := influxdb_common.NewClient(sink.c) + if err != nil { + return err + } + sink.client = client + } + + return nil +} + +func (sink *influxdbSink) createDatabase() error { + if err := sink.ensureClient(); err != nil { + return err + } + + if sink.dbExists { + return nil + } + q := influxdb.Query{ + Command: fmt.Sprintf("CREATE DATABASE %s", sink.c.DbName), + } + if resp, err := sink.client.Query(q); err != nil { + if !(resp != nil && resp.Err != nil && strings.Contains(resp.Err.Error(), "already exists")) { + return fmt.Errorf("Database creation failed: %v", err) + } + } + sink.dbExists = true + glog.Infof("Created database %q on influxDB server at %q", sink.c.DbName, sink.c.Host) + return nil +} + +// Returns a thread-compatible implementation of influxdb interactions. +func new(c influxdb_common.InfluxdbConfig) core.DataSink { + client, err := influxdb_common.NewClient(c) + if err != nil { + glog.Errorf("issues while creating an InfluxDB sink: %v, will retry on use", err) + } + return &influxdbSink{ + client: client, // can be nil + c: c, + } +} + +func CreateInfluxdbSink(uri *url.URL) (core.DataSink, error) { + config, err := influxdb_common.BuildConfig(uri) + if err != nil { + return nil, err + } + sink := new(*config) + glog.Infof("created influxdb sink with options: host:%s user:%s db:%s", config.Host, config.User, config.DbName) + return sink, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_historical.go b/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_historical.go new file mode 100644 index 0000000000..32552b5b84 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_historical.go @@ -0,0 +1,729 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "encoding/json" + "fmt" + "regexp" + "strings" + "time" + "unicode" + + "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" + influxdb "github.com/influxdata/influxdb/client" + influx_models "github.com/influxdata/influxdb/models" +) + +// Historical indicates that this sink supports being used as a HistoricalSource +func (sink *influxdbSink) Historical() core.HistoricalSource { + return sink +} + +// implementation of HistoricalSource for influxdbSink + +// Kube pod and namespace names are limitted to [a-zA-Z0-9-.], while docker also allows +// underscores, so only allow these those characters. When Influx actually supports bound +// paramaters, this will be less necessary. +var nameAllowedChars = regexp.MustCompile("^[a-zA-Z0-9_.-]+$") + +// metric names are restricted to prevent injection attacks +var metricAllowedChars = regexp.MustCompile("^[a-zA-Z0-9_./:-]+$") + +// checkSanitizedKey errors out if invalid characters are found in the key, since InfluxDB does not widely +// support bound parameters yet (see https://github.com/influxdata/influxdb/pull/6634) and we need to +// sanitize our inputs. +func (sink *influxdbSink) checkSanitizedKey(key *core.HistoricalKey) error { + if key.NodeName != "" && !nameAllowedChars.MatchString(key.NodeName) { + return fmt.Errorf("Invalid node name %q", key.NodeName) + } + + if key.NamespaceName != "" && !nameAllowedChars.MatchString(key.NamespaceName) { + return fmt.Errorf("Invalid namespace name %q", key.NamespaceName) + } + + if key.PodName != "" && !nameAllowedChars.MatchString(key.PodName) { + return fmt.Errorf("Invalid pod name %q", key.PodName) + } + + // NB: this prevents access to some of the free containers with slashes in their name + // (e.g. system.slice/foo.bar), but the Heapster API seems to choke on the slashes anyway + if key.ContainerName != "" && !nameAllowedChars.MatchString(key.ContainerName) { + return fmt.Errorf("Invalid container name %q", key.ContainerName) + } + + if key.PodId != "" && !nameAllowedChars.MatchString(key.PodId) { + return fmt.Errorf("Invalid pod id %q", key.PodId) + } + + return nil +} + +// checkSanitizedMetricName errors out if invalid characters are found in the metric name, since InfluxDB +// does not widely support bound parameters yet, and we need to sanitize our inputs. +func (sink *influxdbSink) checkSanitizedMetricName(name string) error { + if !metricAllowedChars.MatchString(name) { + return fmt.Errorf("Invalid metric name %q", name) + } + + return nil +} + +// checkSanitizedMetricLabels errors out if invalid characters are found in the label name or label value, since +// InfluxDb does not widely support bound parameters yet, and we need to sanitize our inputs. +func (sink *influxdbSink) checkSanitizedMetricLabels(labels map[string]string) error { + // label names have the same restrictions as metric names, here + for k, v := range labels { + if !metricAllowedChars.MatchString(k) { + return fmt.Errorf("Invalid label name %q", k) + } + + // for metric values, we're somewhat more permissive. We allow any + // Printable unicode character, except quotation marks, which are used + // to delimit things. + if strings.ContainsRune(v, '"') || strings.ContainsRune(v, '\'') { + return fmt.Errorf("Invalid label value %q", v) + } + + for _, runeVal := range v { + if !unicode.IsPrint(runeVal) { + return fmt.Errorf("Invalid label value %q", v) + } + } + } + + return nil +} + +// aggregationFunc converts an aggregation name into the equivalent call to an InfluxQL +// aggregation function +func (sink *influxdbSink) aggregationFunc(aggregationName core.AggregationType, fieldName string) string { + switch aggregationName { + case core.AggregationTypeAverage: + return fmt.Sprintf("MEAN(%q)", fieldName) + case core.AggregationTypeMaximum: + return fmt.Sprintf("MAX(%q)", fieldName) + case core.AggregationTypeMinimum: + return fmt.Sprintf("MIN(%q)", fieldName) + case core.AggregationTypeMedian: + return fmt.Sprintf("MEDIAN(%q)", fieldName) + case core.AggregationTypeCount: + return fmt.Sprintf("COUNT(%q)", fieldName) + case core.AggregationTypePercentile50: + return fmt.Sprintf("PERCENTILE(%q, 50)", fieldName) + case core.AggregationTypePercentile95: + return fmt.Sprintf("PERCENTILE(%q, 95)", fieldName) + case core.AggregationTypePercentile99: + return fmt.Sprintf("PERCENTILE(%q, 99)", fieldName) + } + + // This should have been checked by the API level, so something's seriously wrong here + panic(fmt.Sprintf("Unknown aggregation type %q", aggregationName)) +} + +// keyToSelector converts a HistoricalKey to a InfluxQL predicate +func (sink *influxdbSink) keyToSelector(key core.HistoricalKey) string { + typeSel := fmt.Sprintf("type = '%s'", key.ObjectType) + switch key.ObjectType { + case core.MetricSetTypeNode: + return fmt.Sprintf("%s AND %s = '%s'", typeSel, core.LabelNodename.Key, key.NodeName) + case core.MetricSetTypeSystemContainer: + return fmt.Sprintf("%s AND %s = '%s' AND %s = '%s'", typeSel, core.LabelContainerName.Key, key.ContainerName, core.LabelNodename.Key, key.NodeName) + case core.MetricSetTypeCluster: + return typeSel + case core.MetricSetTypeNamespace: + return fmt.Sprintf("%s AND %s = '%s'", typeSel, core.LabelNamespaceName.Key, key.NamespaceName) + case core.MetricSetTypePod: + if key.PodId != "" { + return fmt.Sprintf("%s AND %s = '%s'", typeSel, core.LabelPodId.Key, key.PodId) + } else { + return fmt.Sprintf("%s AND %s = '%s' AND %s = '%s'", typeSel, core.LabelNamespaceName.Key, key.NamespaceName, core.LabelPodName.Key, key.PodName) + } + case core.MetricSetTypePodContainer: + if key.PodId != "" { + return fmt.Sprintf("%s AND %s = '%s' AND %s = '%s'", typeSel, core.LabelPodId.Key, key.PodId, core.LabelContainerName.Key, key.ContainerName) + } else { + return fmt.Sprintf("%s AND %s = '%s' AND %s = '%s' AND %s = '%s'", typeSel, core.LabelNamespaceName.Key, key.NamespaceName, core.LabelPodName.Key, key.PodName, core.LabelContainerName.Key, key.ContainerName) + } + } + + // These are assigned by the API, so it shouldn't be possible to reach this unless things are really broken + panic(fmt.Sprintf("Unknown metric type %q", key.ObjectType)) +} + +// labelsToPredicate composes an InfluxQL predicate based on the given map of labels +func (sink *influxdbSink) labelsToPredicate(labels map[string]string) string { + if len(labels) == 0 { + return "" + } + + parts := make([]string, 0, len(labels)) + for k, v := range labels { + parts = append(parts, fmt.Sprintf("%q = '%s'", k, v)) + } + + return strings.Join(parts, " AND ") +} + +// metricToSeriesAndField retrieves the appropriate field name and series name for a given metric +// (this varies depending on whether or not WithFields is enabled) +func (sink *influxdbSink) metricToSeriesAndField(metricName string) (string, string) { + if sink.c.WithFields { + seriesName := strings.SplitN(metricName, "/", 2) + if len(seriesName) > 1 { + return seriesName[0], seriesName[1] + } else { + return seriesName[0], "value" + } + } else { + return metricName, "value" + } +} + +// composeRawQuery creates the InfluxQL query to fetch the given metric values +func (sink *influxdbSink) composeRawQuery(metricName string, labels map[string]string, metricKeys []core.HistoricalKey, start, end time.Time) string { + seriesName, fieldName := sink.metricToSeriesAndField(metricName) + + queries := make([]string, len(metricKeys)) + for i, key := range metricKeys { + pred := sink.keyToSelector(key) + if labels != nil { + pred += fmt.Sprintf(" AND %s", sink.labelsToPredicate(labels)) + } + if !start.IsZero() { + pred += fmt.Sprintf(" AND time > '%s'", start.Format(time.RFC3339)) + } + if !end.IsZero() { + pred += fmt.Sprintf(" AND time < '%s'", end.Format(time.RFC3339)) + } + queries[i] = fmt.Sprintf("SELECT time, %q FROM %q WHERE %s", fieldName, seriesName, pred) + } + + return strings.Join(queries, "; ") +} + +// parseRawQueryRow parses a set of timestamped metric values from unstructured JSON output into the +// appropriate Heapster form +func (sink *influxdbSink) parseRawQueryRow(rawRow influx_models.Row) ([]core.TimestampedMetricValue, error) { + vals := make([]core.TimestampedMetricValue, len(rawRow.Values)) + wasInt := make(map[string]bool, 1) + for i, rawVal := range rawRow.Values { + val := core.TimestampedMetricValue{} + + if ts, err := time.Parse(time.RFC3339, rawVal[0].(string)); err != nil { + return nil, fmt.Errorf("Unable to parse timestamp %q in series %q", rawVal[0].(string), rawRow.Name) + } else { + val.Timestamp = ts + } + + if err := tryParseMetricValue("value", rawVal, &val.MetricValue, 1, wasInt); err != nil { + glog.Errorf("Unable to parse field \"value\" in series %q: %v", rawRow.Name, err) + return nil, fmt.Errorf("Unable to parse values in series %q", rawRow.Name) + } + + vals[i] = val + } + + if wasInt["value"] { + for i := range vals { + vals[i].MetricValue.ValueType = core.ValueInt64 + } + } else { + for i := range vals { + vals[i].MetricValue.ValueType = core.ValueFloat + } + } + + return vals, nil +} + +// GetMetric retrieves the given metric for one or more objects (specified by metricKeys) of +// the same type, within the given time interval +func (sink *influxdbSink) GetMetric(metricName string, metricKeys []core.HistoricalKey, start, end time.Time) (map[core.HistoricalKey][]core.TimestampedMetricValue, error) { + for _, key := range metricKeys { + if err := sink.checkSanitizedKey(&key); err != nil { + return nil, err + } + } + + if err := sink.checkSanitizedMetricName(metricName); err != nil { + return nil, err + } + + query := sink.composeRawQuery(metricName, nil, metricKeys, start, end) + + sink.RLock() + defer sink.RUnlock() + + resp, err := sink.runQuery(query) + if err != nil { + return nil, err + } + + res := make(map[core.HistoricalKey][]core.TimestampedMetricValue, len(metricKeys)) + for i, key := range metricKeys { + if len(resp[i].Series) < 1 { + return nil, fmt.Errorf("No results for metric %q describing %q", metricName, key.String()) + } + + vals, err := sink.parseRawQueryRow(resp[i].Series[0]) + if err != nil { + return nil, err + } + res[key] = vals + } + + return res, nil +} + +// GetLabeledMetric retrieves the given labeled metric for one or more objects (specified by metricKeys) of +// the same type, within the given time interval +func (sink *influxdbSink) GetLabeledMetric(metricName string, labels map[string]string, metricKeys []core.HistoricalKey, start, end time.Time) (map[core.HistoricalKey][]core.TimestampedMetricValue, error) { + for _, key := range metricKeys { + if err := sink.checkSanitizedKey(&key); err != nil { + return nil, err + } + } + + if err := sink.checkSanitizedMetricName(metricName); err != nil { + return nil, err + } + + if err := sink.checkSanitizedMetricLabels(labels); err != nil { + return nil, err + } + + query := sink.composeRawQuery(metricName, labels, metricKeys, start, end) + + sink.RLock() + defer sink.RUnlock() + + resp, err := sink.runQuery(query) + if err != nil { + return nil, err + } + + res := make(map[core.HistoricalKey][]core.TimestampedMetricValue, len(metricKeys)) + for i, key := range metricKeys { + if len(resp[i].Series) < 1 { + return nil, fmt.Errorf("No results for metric %q describing %q", metricName, key.String()) + } + + vals, err := sink.parseRawQueryRow(resp[i].Series[0]) + if err != nil { + return nil, err + } + res[key] = vals + } + + return res, nil +} + +// composeAggregateQuery creates the InfluxQL query to fetch the given aggregation values +func (sink *influxdbSink) composeAggregateQuery(metricName string, labels map[string]string, aggregations []core.AggregationType, metricKeys []core.HistoricalKey, start, end time.Time, bucketSize time.Duration) string { + seriesName, fieldName := sink.metricToSeriesAndField(metricName) + + var bucketSizeNanoSeconds int64 = 0 + if bucketSize != 0 { + bucketSizeNanoSeconds = int64(bucketSize.Nanoseconds() / int64(time.Microsecond/time.Nanosecond)) + } + + queries := make([]string, len(metricKeys)) + for i, key := range metricKeys { + pred := sink.keyToSelector(key) + if labels != nil { + pred += fmt.Sprintf(" AND %s", sink.labelsToPredicate(labels)) + } + if !start.IsZero() { + pred += fmt.Sprintf(" AND time > '%s'", start.Format(time.RFC3339)) + } + if !end.IsZero() { + pred += fmt.Sprintf(" AND time < '%s'", end.Format(time.RFC3339)) + } + + aggParts := make([]string, len(aggregations)) + for i, agg := range aggregations { + aggParts[i] = sink.aggregationFunc(agg, fieldName) + } + + queries[i] = fmt.Sprintf("SELECT %s FROM %q WHERE %s", strings.Join(aggParts, ", "), seriesName, pred) + + if bucketSize != 0 { + // group by time requires we have at least one time bound + if start.IsZero() && end.IsZero() { + queries[i] += fmt.Sprintf(" AND time < now()") + } + + // fill(none) makes sure we skip data points will null values (otherwise we'll get a *bunch* of null + // values when we go back beyond the time where we started collecting data). + queries[i] += fmt.Sprintf(" GROUP BY time(%vu) fill(none)", bucketSizeNanoSeconds) + } + } + + return strings.Join(queries, "; ") +} + +// parseRawQueryRow parses a set of timestamped aggregation values from unstructured JSON output into the +// appropriate Heapster form +func (sink *influxdbSink) parseAggregateQueryRow(rawRow influx_models.Row, aggregationLookup map[core.AggregationType]int, bucketSize time.Duration) ([]core.TimestampedAggregationValue, error) { + vals := make([]core.TimestampedAggregationValue, len(rawRow.Values)) + wasInt := make(map[string]bool, len(aggregationLookup)) + + for i, rawVal := range rawRow.Values { + val := core.TimestampedAggregationValue{ + BucketSize: bucketSize, + AggregationValue: core.AggregationValue{ + Aggregations: map[core.AggregationType]core.MetricValue{}, + }, + } + + if ts, err := time.Parse(time.RFC3339, rawVal[0].(string)); err != nil { + return nil, fmt.Errorf("Unable to parse timestamp %q in series %q", rawVal[0].(string), rawRow.Name) + } else { + val.Timestamp = ts + } + + // The Influx client decods numeric fields to json.Number (a string), so we have to try decoding to both types of numbers + + // Count is always a uint64 + if countIndex, ok := aggregationLookup[core.AggregationTypeCount]; ok { + if err := json.Unmarshal([]byte(rawVal[countIndex].(json.Number).String()), &val.Count); err != nil { + glog.Errorf("Unable to parse count value in series %q: %v", rawRow.Name, err) + return nil, fmt.Errorf("Unable to parse values in series %q", rawRow.Name) + } + } + + // The rest of the aggregation values can be either float or int, so attempt to parse both + if err := populateAggregations(rawRow.Name, rawVal, &val, aggregationLookup, wasInt); err != nil { + return nil, err + } + + vals[i] = val + } + + // figure out whether each aggregation was full of float values, or int values + setAggregationValueTypes(vals, wasInt) + + return vals, nil +} + +// GetAggregation fetches the given aggregations for one or more objects (specified by metricKeys) of +// the same type, within the given time interval, calculated over a series of buckets +func (sink *influxdbSink) GetAggregation(metricName string, aggregations []core.AggregationType, metricKeys []core.HistoricalKey, start, end time.Time, bucketSize time.Duration) (map[core.HistoricalKey][]core.TimestampedAggregationValue, error) { + for _, key := range metricKeys { + if err := sink.checkSanitizedKey(&key); err != nil { + return nil, err + } + } + + if err := sink.checkSanitizedMetricName(metricName); err != nil { + return nil, err + } + + // make it easy to look up where the different aggregations are in the list + aggregationLookup := make(map[core.AggregationType]int, len(aggregations)) + for i, agg := range aggregations { + aggregationLookup[agg] = i + 1 + } + + query := sink.composeAggregateQuery(metricName, nil, aggregations, metricKeys, start, end, bucketSize) + + sink.RLock() + defer sink.RUnlock() + + resp, err := sink.runQuery(query) + if err != nil { + return nil, err + } + + // TODO: when there are too many points (e.g. certain times when a start time is not specified), Influx will sometimes return only a single bucket + // instead of returning an error. We should detect this case and return an error ourselves (or maybe just require a start time at the API level) + res := make(map[core.HistoricalKey][]core.TimestampedAggregationValue, len(metricKeys)) + for i, key := range metricKeys { + if len(resp[i].Series) < 1 { + return nil, fmt.Errorf("No results for metric %q describing %q", metricName, key.String()) + } + + vals, err := sink.parseAggregateQueryRow(resp[i].Series[0], aggregationLookup, bucketSize) + if err != nil { + return nil, err + } + res[key] = vals + } + + return res, nil +} + +// GetLabeledAggregation fetches the given aggregations (on labeled metrics) for one or more objects +// (specified by metricKeys) of the same type, within the given time interval, calculated over a series of buckets +func (sink *influxdbSink) GetLabeledAggregation(metricName string, labels map[string]string, aggregations []core.AggregationType, metricKeys []core.HistoricalKey, start, end time.Time, bucketSize time.Duration) (map[core.HistoricalKey][]core.TimestampedAggregationValue, error) { + for _, key := range metricKeys { + if err := sink.checkSanitizedKey(&key); err != nil { + return nil, err + } + } + + if err := sink.checkSanitizedMetricName(metricName); err != nil { + return nil, err + } + + if err := sink.checkSanitizedMetricLabels(labels); err != nil { + return nil, err + } + + // make it easy to look up where the different aggregations are in the list + aggregationLookup := make(map[core.AggregationType]int, len(aggregations)) + for i, agg := range aggregations { + aggregationLookup[agg] = i + 1 + } + + query := sink.composeAggregateQuery(metricName, labels, aggregations, metricKeys, start, end, bucketSize) + + sink.RLock() + defer sink.RUnlock() + + resp, err := sink.runQuery(query) + if err != nil { + return nil, err + } + + // TODO: when there are too many points (e.g. certain times when a start time is not specified), Influx will sometimes return only a single bucket + // instead of returning an error. We should detect this case and return an error ourselves (or maybe just require a start time at the API level) + res := make(map[core.HistoricalKey][]core.TimestampedAggregationValue, len(metricKeys)) + for i, key := range metricKeys { + if len(resp[i].Series) < 1 { + return nil, fmt.Errorf("No results for metric %q describing %q", metricName, key.String()) + } + + vals, err := sink.parseAggregateQueryRow(resp[i].Series[0], aggregationLookup, bucketSize) + if err != nil { + return nil, err + } + res[key] = vals + } + + return res, nil +} + +// setAggregationValueIfPresent checks to to if the given metric value is present in the list of raw values, and if so, +// copies it to the output format +func setAggregationValueIfPresent(aggName core.AggregationType, rawVal []interface{}, aggregations *core.AggregationValue, indexLookup map[core.AggregationType]int, wasInt map[string]bool) error { + if fieldIndex, ok := indexLookup[aggName]; ok { + targetValue := &core.MetricValue{} + if err := tryParseMetricValue(string(aggName), rawVal, targetValue, fieldIndex, wasInt); err != nil { + return err + } + + aggregations.Aggregations[aggName] = *targetValue + } + + return nil +} + +// tryParseMetricValue attempts to parse a raw metric value into the appropriate go type. +func tryParseMetricValue(aggName string, rawVal []interface{}, targetValue *core.MetricValue, fieldIndex int, wasInt map[string]bool) error { + // the Influx client decodes numeric fields to json.Number (a string), so we have to deal with that -- + // assume, starting off, that values may be either float or int. Try int until we fail once, and always + // try float. At the end, figure out which is which. + + var rv string + if rvN, ok := rawVal[fieldIndex].(json.Number); !ok { + return fmt.Errorf("Value %q of metric %q was not a json.Number", rawVal[fieldIndex], aggName) + } else { + rv = rvN.String() + } + + tryInt := false + isInt, triedBefore := wasInt[aggName] + tryInt = isInt || !triedBefore + + if tryInt { + if err := json.Unmarshal([]byte(rv), &targetValue.IntValue); err != nil { + wasInt[aggName] = false + } else { + wasInt[aggName] = true + } + } + + if err := json.Unmarshal([]byte(rv), &targetValue.FloatValue); err != nil { + return err + } + + return nil +} + +// GetMetricNames retrieves the available metric names for the given object +func (sink *influxdbSink) GetMetricNames(metricKey core.HistoricalKey) ([]string, error) { + if err := sink.checkSanitizedKey(&metricKey); err != nil { + return nil, err + } + return sink.stringListQuery(fmt.Sprintf("SHOW MEASUREMENTS WHERE %s", sink.keyToSelector(metricKey)), "Unable to list available metrics") +} + +// GetNodes retrieves the list of nodes in the cluster +func (sink *influxdbSink) GetNodes() ([]string, error) { + return sink.stringListQuery(fmt.Sprintf("SHOW TAG VALUES WITH KEY = %s", core.LabelNodename.Key), "Unable to list all nodes") +} + +// GetNamespaces retrieves the list of namespaces in the cluster +func (sink *influxdbSink) GetNamespaces() ([]string, error) { + return sink.stringListQuery(fmt.Sprintf("SHOW TAG VALUES WITH KEY = %s", core.LabelNamespaceName.Key), "Unable to list all namespaces") +} + +// GetPodsFromNamespace retrieves the list of pods in a given namespace +func (sink *influxdbSink) GetPodsFromNamespace(namespace string) ([]string, error) { + if !nameAllowedChars.MatchString(namespace) { + return nil, fmt.Errorf("Invalid namespace name %q", namespace) + } + // This is a bit difficult for the influx query language, so we cheat a bit here -- + // we just get all series for the uptime measurement for pods which match our namespace + // (any measurement should work here, though) + q := fmt.Sprintf("SHOW SERIES FROM %q WHERE %s = '%s' AND type = '%s'", core.MetricUptime.MetricDescriptor.Name, core.LabelNamespaceName.Key, namespace, core.MetricSetTypePod) + return sink.stringListQueryCol(q, core.LabelPodName.Key, fmt.Sprintf("Unable to list pods in namespace %q", namespace)) +} + +// GetSystemContainersFromNode retrieves the list of free containers for a given node +func (sink *influxdbSink) GetSystemContainersFromNode(node string) ([]string, error) { + if !nameAllowedChars.MatchString(node) { + return nil, fmt.Errorf("Invalid node name %q", node) + } + // This is a bit difficult for the influx query language, so we cheat a bit here -- + // we just get all series for the uptime measurement for system containers on our node + // (any measurement should work here, though) + q := fmt.Sprintf("SHOW SERIES FROM %q WHERE %s = '%s' AND type = '%s'", core.MetricUptime.MetricDescriptor.Name, core.LabelNodename.Key, node, core.MetricSetTypeSystemContainer) + return sink.stringListQueryCol(q, core.LabelContainerName.Key, fmt.Sprintf("Unable to list system containers on node %q", node)) +} + +// stringListQueryCol runs the given query, and returns all results from the given column as a string list +func (sink *influxdbSink) stringListQueryCol(q, colName string, errStr string) ([]string, error) { + sink.RLock() + defer sink.RUnlock() + + resp, err := sink.runQuery(q) + if err != nil { + return nil, fmt.Errorf(errStr) + } + + if len(resp[0].Series) < 1 { + return nil, fmt.Errorf(errStr) + } + + colInd := -1 + for i, col := range resp[0].Series[0].Columns { + if col == colName { + colInd = i + break + } + } + + if colInd == -1 { + glog.Errorf("%s: results did not contain the %q column", errStr, core.LabelPodName.Key) + return nil, fmt.Errorf(errStr) + } + + res := make([]string, len(resp[0].Series[0].Values)) + for i, rv := range resp[0].Series[0].Values { + res[i] = rv[colInd].(string) + } + return res, nil +} + +// stringListQuery runs the given query, and returns all results from the first column as a string list +func (sink *influxdbSink) stringListQuery(q string, errStr string) ([]string, error) { + sink.RLock() + defer sink.RUnlock() + + resp, err := sink.runQuery(q) + if err != nil { + return nil, fmt.Errorf(errStr) + } + + if len(resp[0].Series) < 1 { + return nil, fmt.Errorf(errStr) + } + + res := make([]string, len(resp[0].Series[0].Values)) + for i, rv := range resp[0].Series[0].Values { + res[i] = rv[0].(string) + } + return res, nil +} + +// runQuery executes the given query against InfluxDB (using the default database for this sink) +// The caller is responsible for locking the sink before use. +func (sink *influxdbSink) runQuery(queryStr string) ([]influxdb.Result, error) { + // ensure we have a valid client handle before attempting to use it + if err := sink.ensureClient(); err != nil { + glog.Errorf("Unable to ensure InfluxDB client is present: %v", err) + return nil, fmt.Errorf("unable to run query: unable to connect to database") + } + + q := influxdb.Query{ + Command: queryStr, + Database: sink.c.DbName, + } + + glog.V(4).Infof("Executing query %q against database %q", q.Command, q.Database) + + resp, err := sink.client.Query(q) + if err != nil { + glog.Errorf("Unable to perform query %q against database %q: %v", q.Command, q.Database, err) + return nil, err + } else if resp.Error() != nil { + glog.Errorf("Unable to perform query %q against database %q: %v", q.Command, q.Database, resp.Error()) + return nil, resp.Error() + } + + if len(resp.Results) < 1 { + glog.Errorf("Unable to perform query %q against database %q: no results returned", q.Command, q.Database) + return nil, fmt.Errorf("No results returned") + } + + return resp.Results, nil +} + +// populateAggregations extracts aggregation values from a given data point +func populateAggregations(rawRowName string, rawVal []interface{}, val *core.TimestampedAggregationValue, aggregationLookup map[core.AggregationType]int, wasInt map[string]bool) error { + for _, aggregation := range core.MultiTypedAggregations { + if err := setAggregationValueIfPresent(aggregation, rawVal, &val.AggregationValue, aggregationLookup, wasInt); err != nil { + glog.Errorf("Unable to parse field %q in series %q: %v", aggregation, rawRowName, err) + return fmt.Errorf("Unable to parse values in series %q", rawRowName) + } + } + + return nil +} + +// setAggregationValueTypes inspects a set of aggregation values and figures out whether each aggregation value +// returned as a float column, or an int column +func setAggregationValueTypes(vals []core.TimestampedAggregationValue, wasInt map[string]bool) { + for _, aggregation := range core.MultiTypedAggregations { + if isInt, ok := wasInt[string(aggregation)]; ok && isInt { + for i := range vals { + val := vals[i].Aggregations[aggregation] + val.ValueType = core.ValueInt64 + vals[i].Aggregations[aggregation] = val + } + } else if ok { + for i := range vals { + val := vals[i].Aggregations[aggregation] + val.ValueType = core.ValueFloat + vals[i].Aggregations[aggregation] = val + } + } + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_test.go b/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_test.go new file mode 100644 index 0000000000..4b6d080eeb --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/influxdb/influxdb_test.go @@ -0,0 +1,583 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influxdb + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "net/http/httptest" + "net/url" + + influx_models "github.com/influxdata/influxdb/models" + "github.com/stretchr/testify/assert" + influxdb_common "k8s.io/heapster/common/influxdb" + "k8s.io/heapster/metrics/core" + util "k8s.io/kubernetes/pkg/util/testing" +) + +type fakeInfluxDBDataSink struct { + core.DataSink + fakeDbClient *influxdb_common.FakeInfluxDBClient +} + +func newRawInfluxSink() *influxdbSink { + return &influxdbSink{ + client: influxdb_common.Client, + c: influxdb_common.Config, + } +} + +func NewFakeSink() fakeInfluxDBDataSink { + return fakeInfluxDBDataSink{ + newRawInfluxSink(), + influxdb_common.Client, + } +} +func TestStoreDataEmptyInput(t *testing.T) { + fakeSink := NewFakeSink() + dataBatch := core.DataBatch{} + fakeSink.ExportData(&dataBatch) + assert.Equal(t, 0, len(fakeSink.fakeDbClient.Pnts)) +} + +func TestStoreMultipleDataInput(t *testing.T) { + fakeSink := NewFakeSink() + timestamp := time.Now() + + l := make(map[string]string) + l["namespace_id"] = "123" + l["container_name"] = "/system.slice/-.mount" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l2 := make(map[string]string) + l2["namespace_id"] = "123" + l2["container_name"] = "/system.slice/dbus.service" + l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l3 := make(map[string]string) + l3["namespace_id"] = "123" + l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l4 := make(map[string]string) + l4["namespace_id"] = "" + l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l5 := make(map[string]string) + l5["namespace_id"] = "123" + l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + metricSet1 := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + "/system.slice/-.mount//cpu/limit": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet2 := core.MetricSet{ + Labels: l2, + MetricValues: map[string]core.MetricValue{ + "/system.slice/dbus.service//cpu/usage": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet3 := core.MetricSet{ + Labels: l3, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet4 := core.MetricSet{ + Labels: l4, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet5 := core.MetricSet{ + Labels: l5, + MetricValues: map[string]core.MetricValue{ + "removeme": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + data := core.DataBatch{ + Timestamp: timestamp, + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet1, + "pod2": &metricSet2, + "pod3": &metricSet3, + "pod4": &metricSet4, + "pod5": &metricSet5, + }, + } + + fakeSink.ExportData(&data) + assert.Equal(t, 5, len(fakeSink.fakeDbClient.Pnts)) +} + +func TestCreateInfluxdbSink(t *testing.T) { + handler := util.FakeHandler{ + StatusCode: 200, + RequestBody: "", + ResponseBody: "", + T: t, + } + server := httptest.NewServer(&handler) + defer server.Close() + + stubInfluxDBUrl, err := url.Parse(server.URL) + assert.NoError(t, err) + + //create influxdb sink + sink, err := CreateInfluxdbSink(stubInfluxDBUrl) + assert.NoError(t, err) + + //check sink name + assert.Equal(t, sink.Name(), "InfluxDB Sink") +} + +func makeRow(results [][]string) influx_models.Row { + resRow := influx_models.Row{ + Values: make([][]interface{}, len(results)), + } + + for setInd, valSet := range results { + outVals := make([]interface{}, len(valSet)) + for valInd, val := range valSet { + if valInd == 0 { + // timestamp should just be a string + outVals[valInd] = val + } else { + outVals[valInd] = json.Number(val) + } + } + resRow.Values[setInd] = outVals + } + + return resRow +} + +func checkMetricVal(expected, actual core.MetricValue) bool { + if expected.ValueType != actual.ValueType { + return false + } + + // only check the relevant value type + switch expected.ValueType { + case core.ValueFloat: + return expected.FloatValue == actual.FloatValue + case core.ValueInt64: + return expected.IntValue == actual.IntValue + default: + return expected == actual + } +} + +func TestHistoricalMissingResponses(t *testing.T) { + sink := newRawInfluxSink() + + podKeys := []core.HistoricalKey{ + {ObjectType: core.MetricSetTypePod, NamespaceName: "cheese", PodName: "cheddar"}, + {ObjectType: core.MetricSetTypePod, NamespaceName: "cheese", PodName: "swiss"}, + } + labels := map[string]string{"crackers": "ritz"} + + errStr := fmt.Sprintf("No results for metric %q describing %q", "cpu/usage_rate", podKeys[0].String()) + + _, err := sink.GetMetric("cpu/usage_rate", podKeys, time.Now().Add(-5*time.Minute), time.Now()) + assert.EqualError(t, err, errStr) + + _, err = sink.GetLabeledMetric("cpu/usage_rate", labels, podKeys, time.Now().Add(-5*time.Minute), time.Now()) + assert.EqualError(t, err, errStr) + + _, err = sink.GetAggregation("cpu/usage_rate", []core.AggregationType{core.AggregationTypeAverage}, podKeys, time.Now().Add(-5*time.Minute), time.Now(), 5*time.Minute) + assert.EqualError(t, err, errStr) + + _, err = sink.GetLabeledAggregation("cpu/usage_rate", labels, []core.AggregationType{core.AggregationTypeAverage}, podKeys, time.Now().Add(-5*time.Minute), time.Now(), 5*time.Minute) + assert.EqualError(t, err, errStr) +} + +func TestHistoricalInfluxRawMetricsParsing(t *testing.T) { + // in order to just test the parsing, we just go directly to the sink type + sink := newRawInfluxSink() + + baseTime := time.Time{} + + rawTests := []struct { + name string + rawData influx_models.Row + expectedResults []core.TimestampedMetricValue + expectedError bool + }{ + { + name: "all-integer data", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "1234", + }, + { + baseTime.Add(48 * time.Hour).Format(time.RFC3339), + "5678", + }, + }), + expectedResults: []core.TimestampedMetricValue{ + { + Timestamp: baseTime.Add(24 * time.Hour), + MetricValue: core.MetricValue{IntValue: 1234, ValueType: core.ValueInt64}, + }, + { + Timestamp: baseTime.Add(48 * time.Hour), + MetricValue: core.MetricValue{IntValue: 5678, ValueType: core.ValueInt64}, + }, + }, + }, + { + name: "all-float data", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "1.23e10", + }, + { + baseTime.Add(48 * time.Hour).Format(time.RFC3339), + "4.56e11", + }, + }), + expectedResults: []core.TimestampedMetricValue{ + { + Timestamp: baseTime.Add(24 * time.Hour), + MetricValue: core.MetricValue{FloatValue: 12300000000.0, ValueType: core.ValueFloat}, + }, + { + Timestamp: baseTime.Add(48 * time.Hour), + MetricValue: core.MetricValue{FloatValue: 456000000000.0, ValueType: core.ValueFloat}, + }, + }, + }, + { + name: "mixed data", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "123", + }, + { + baseTime.Add(48 * time.Hour).Format(time.RFC3339), + "4.56e11", + }, + }), + expectedResults: []core.TimestampedMetricValue{ + { + Timestamp: baseTime.Add(24 * time.Hour), + MetricValue: core.MetricValue{FloatValue: 123.0, ValueType: core.ValueFloat}, + }, + { + Timestamp: baseTime.Add(48 * time.Hour), + MetricValue: core.MetricValue{FloatValue: 456000000000.0, ValueType: core.ValueFloat}, + }, + }, + }, + { + name: "data with invalid value", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "true", + }, + }), + expectedError: true, + }, + } + +RAWTESTLOOP: + for _, test := range rawTests { + parsedRawResults, err := sink.parseRawQueryRow(test.rawData) + if (err != nil) != test.expectedError { + t.Errorf("When parsing raw %s: expected error %v != actual error %v", test.name, test.expectedError, err) + continue RAWTESTLOOP + } + + if len(parsedRawResults) != len(test.expectedResults) { + t.Errorf("When parsing raw %s: expected results %#v != actual results %#v", test.name, test.expectedResults, parsedRawResults) + continue RAWTESTLOOP + } + + for i, metricVal := range parsedRawResults { + if !test.expectedResults[i].Timestamp.Equal(metricVal.Timestamp) { + t.Errorf("When parsing raw %s: expected results %#v != actual results %#v", test.name, test.expectedResults, parsedRawResults) + continue RAWTESTLOOP + } + + if !checkMetricVal(test.expectedResults[i].MetricValue, metricVal.MetricValue) { + t.Errorf("When parsing raw %s: expected results %#v != actual results %#v", test.name, test.expectedResults, parsedRawResults) + continue RAWTESTLOOP + } + } + } + + var countVal2 uint64 = 2 + aggregatedTests := []struct { + name string + rawData influx_models.Row + expectedResults []core.TimestampedAggregationValue + expectedError bool + }{ + { + name: "all-integer data", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "2", + "1234", + }, + { + baseTime.Add(48 * time.Hour).Format(time.RFC3339), + "2", + "5678", + }, + }), + expectedResults: []core.TimestampedAggregationValue{ + { + Timestamp: baseTime.Add(24 * time.Hour), + AggregationValue: core.AggregationValue{ + Count: &countVal2, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: {IntValue: 1234, ValueType: core.ValueInt64}, + }, + }, + }, + { + Timestamp: baseTime.Add(48 * time.Hour), + AggregationValue: core.AggregationValue{ + Count: &countVal2, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: {IntValue: 5678, ValueType: core.ValueInt64}, + }, + }, + }, + }, + }, + { + name: "all-float data", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "2", + "1.23e10", + }, + { + baseTime.Add(48 * time.Hour).Format(time.RFC3339), + "2", + "4.56e11", + }, + }), + expectedResults: []core.TimestampedAggregationValue{ + { + Timestamp: baseTime.Add(24 * time.Hour), + AggregationValue: core.AggregationValue{ + Count: &countVal2, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: {FloatValue: 12300000000.0, ValueType: core.ValueFloat}, + }, + }, + }, + { + Timestamp: baseTime.Add(48 * time.Hour), + AggregationValue: core.AggregationValue{ + Count: &countVal2, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: {FloatValue: 456000000000.0, ValueType: core.ValueFloat}, + }, + }, + }, + }, + }, + { + name: "mixed data", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "2", + "123", + }, + { + baseTime.Add(48 * time.Hour).Format(time.RFC3339), + "2", + "4.56e11", + }, + }), + expectedResults: []core.TimestampedAggregationValue{ + { + Timestamp: baseTime.Add(24 * time.Hour), + AggregationValue: core.AggregationValue{ + Count: &countVal2, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: {FloatValue: 123.0, ValueType: core.ValueFloat}, + }, + }, + }, + { + Timestamp: baseTime.Add(48 * time.Hour), + AggregationValue: core.AggregationValue{ + Count: &countVal2, + Aggregations: map[core.AggregationType]core.MetricValue{ + core.AggregationTypeAverage: {FloatValue: 456000000000.0, ValueType: core.ValueFloat}, + }, + }, + }, + }, + }, + { + name: "data with invalid value", + rawData: makeRow([][]string{ + { + baseTime.Add(24 * time.Hour).Format(time.RFC3339), + "2", + "true", + }, + }), + expectedError: true, + }, + } + + aggregationLookup := map[core.AggregationType]int{ + core.AggregationTypeCount: 1, + core.AggregationTypeAverage: 2, + } +AGGTESTLOOP: + for _, test := range aggregatedTests { + parsedAggResults, err := sink.parseAggregateQueryRow(test.rawData, aggregationLookup, 24*time.Hour) + if (err != nil) != test.expectedError { + t.Errorf("When parsing aggregated %s: expected error %v != actual error %v", test.name, test.expectedError, err) + continue AGGTESTLOOP + } + + if len(parsedAggResults) != len(test.expectedResults) { + t.Errorf("When parsing aggregated %s: expected results %#v had a different length from actual results %#v", test.name, test.expectedResults, parsedAggResults) + continue AGGTESTLOOP + } + + for i, metricVal := range parsedAggResults { + expVal := test.expectedResults[i] + if !expVal.Timestamp.Equal(metricVal.Timestamp) { + t.Errorf("When parsing aggregated %s: expected results %#v had a different timestamp from actual results %#v", test.name, expVal, metricVal) + continue AGGTESTLOOP + } + + if len(expVal.Aggregations) != len(metricVal.Aggregations) { + t.Errorf("When parsing aggregated %s: expected results %#v had a number of aggregations from actual results %#v", test.name, expVal, metricVal) + continue AGGTESTLOOP + } + + for aggName, aggVal := range metricVal.Aggregations { + if expAggVal, ok := expVal.Aggregations[aggName]; !ok || !checkMetricVal(expAggVal, aggVal) { + t.Errorf("When parsing aggregated %s: expected results %#v != actual results %#v", test.name, expAggVal, aggVal) + continue AGGTESTLOOP + } + } + } + } +} + +func TestSanitizers(t *testing.T) { + badMetricName := "foo; baz" + goodMetricName := "cheese/types-crackers" + + goodKeyValue := "cheddar.CHEESE-ritz.Crackers_1" + badKeyValue := "foobar'; baz" + + sink := newRawInfluxSink() + + if err := sink.checkSanitizedMetricName(goodMetricName); err != nil { + t.Errorf("Expected %q to be a valid metric name, but it was not: %v", goodMetricName, err) + } + + if err := sink.checkSanitizedMetricName(badMetricName); err == nil { + t.Errorf("Expected %q to be an invalid metric name, but it was valid", badMetricName) + } + + badKeys := []core.HistoricalKey{ + { + NodeName: badKeyValue, + }, + { + NamespaceName: badKeyValue, + }, + { + PodName: badKeyValue, + }, + { + ContainerName: badKeyValue, + }, + { + PodId: badKeyValue, + }, + } + + for _, key := range badKeys { + if err := sink.checkSanitizedKey(&key); err == nil { + t.Errorf("Expected key %#v to be invalid, but it was not", key) + } + } + + goodKeys := []core.HistoricalKey{ + { + NodeName: goodKeyValue, + }, + { + NamespaceName: goodKeyValue, + }, + { + PodName: goodKeyValue, + }, + { + ContainerName: goodKeyValue, + }, + { + PodId: goodKeyValue, + }, + } + + for _, key := range goodKeys { + if err := sink.checkSanitizedKey(&key); err != nil { + t.Errorf("Expected key %#v to be valid, but it was not: %v", key, err) + } + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/kafka/driver.go b/vendor/k8s.io/heapster/metrics/sinks/kafka/driver.go new file mode 100644 index 0000000000..74c397c5f7 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/kafka/driver.go @@ -0,0 +1,170 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kafka + +import ( + "encoding/json" + "fmt" + "net/url" + "sync" + "time" + + "github.com/golang/glog" + "github.com/optiopay/kafka" + "github.com/optiopay/kafka/proto" + "k8s.io/heapster/metrics/core" +) + +const ( + partition = 0 + brokerClientID = "kafka-sink" + brokerDialTimeout = 10 * time.Second + brokerDialRetryLimit = 1 + brokerDialRetryWait = 0 + brokerAllowTopicCreation = true + brokerLeaderRetryLimit = 1 + brokerLeaderRetryWait = 0 + dataTopic = "heapster-metrics" +) + +type KafkaSinkPoint struct { + MetricsName string + MetricsValue interface{} + MetricsTimestamp time.Time + MetricsTags map[string]string +} + +type kafkaSink struct { + producer kafka.Producer + dataTopic string + sync.RWMutex +} + +func (sink *kafkaSink) ExportData(dataBatch *core.DataBatch) { + sink.Lock() + defer sink.Unlock() + + for _, metricSet := range dataBatch.MetricSets { + for metricName, metricValue := range metricSet.MetricValues { + point := KafkaSinkPoint{ + MetricsName: metricName, + MetricsTags: metricSet.Labels, + MetricsValue: map[string]interface{}{ + "value": metricValue.GetValue(), + }, + MetricsTimestamp: dataBatch.Timestamp.UTC(), + } + sink.produceKafkaMessage(point, sink.dataTopic) + } + for _, metric := range metricSet.LabeledMetrics { + labels := make(map[string]string) + for k, v := range metricSet.Labels { + labels[k] = v + } + for k, v := range metric.Labels { + labels[k] = v + } + point := KafkaSinkPoint{ + MetricsName: metric.Name, + MetricsTags: labels, + MetricsValue: map[string]interface{}{ + "value": metric.GetValue(), + }, + MetricsTimestamp: dataBatch.Timestamp.UTC(), + } + sink.produceKafkaMessage(point, sink.dataTopic) + } + } +} + +func (sink *kafkaSink) produceKafkaMessage(dataPoint KafkaSinkPoint, topic string) error { + start := time.Now() + jsonItems, err := json.Marshal(dataPoint) + if err != nil { + return fmt.Errorf("failed to transform the items to json : %s", err) + } + message := &proto.Message{Value: []byte(string(jsonItems))} + _, err = sink.producer.Produce(topic, partition, message) + if err != nil { + return fmt.Errorf("failed to produce message to %s:%d: %s", topic, partition, err) + } + end := time.Now() + glog.V(4).Info("Exported %d data to kafka in %s", len([]byte(string(jsonItems))), end.Sub(start)) + return nil +} + +func (sink *kafkaSink) Name() string { + return "Apache Kafka Sink" +} + +func (sink *kafkaSink) Stop() { + // nothing needs to be done. +} + +// setupProducer returns a producer of kafka server +func setupProducer(sinkBrokerHosts []string, brokerConf kafka.BrokerConf) (kafka.Producer, error) { + glog.V(3).Infof("attempting to setup kafka sink") + broker, err := kafka.Dial(sinkBrokerHosts, brokerConf) + if err != nil { + return nil, fmt.Errorf("failed to connect to kafka cluster: %s", err) + } + defer broker.Close() + + //create kafka producer + conf := kafka.NewProducerConf() + conf.RequiredAcks = proto.RequiredAcksLocal + sinkProducer := broker.Producer(conf) + glog.V(3).Infof("kafka sink setup successfully") + return sinkProducer, nil +} + +func NewKafkaSink(uri *url.URL) (core.DataSink, error) { + opts, err := url.ParseQuery(uri.RawQuery) + if err != nil { + return nil, fmt.Errorf("failed to parser url's query string: %s", err) + } + + var topic string = dataTopic + if len(opts["timeseriestopic"]) > 0 { + topic = opts["timeseriestopic"][0] + } + + var kafkaBrokers []string + if len(opts["brokers"]) < 1 { + return nil, fmt.Errorf("There is no broker assigned for connecting kafka") + } + kafkaBrokers = append(kafkaBrokers, opts["brokers"]...) + glog.V(2).Infof("initializing kafka sink with brokers - %v", kafkaBrokers) + + //structure the config of broker + brokerConf := kafka.NewBrokerConf(brokerClientID) + brokerConf.DialTimeout = brokerDialTimeout + brokerConf.DialRetryLimit = brokerDialRetryLimit + brokerConf.DialRetryWait = brokerDialRetryWait + brokerConf.LeaderRetryLimit = brokerLeaderRetryLimit + brokerConf.LeaderRetryWait = brokerLeaderRetryWait + brokerConf.AllowTopicCreation = true + + // set up producer of kafka server. + sinkProducer, err := setupProducer(kafkaBrokers, brokerConf) + if err != nil { + return nil, fmt.Errorf("Failed to setup Producer: - %v", err) + } + + return &kafkaSink{ + producer: sinkProducer, + dataTopic: topic, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/kafka/driver_test.go b/vendor/k8s.io/heapster/metrics/sinks/kafka/driver_test.go new file mode 100644 index 0000000000..7c247e05ed --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/kafka/driver_test.go @@ -0,0 +1,186 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kafka + +import ( + "fmt" + "testing" + "time" + + "github.com/optiopay/kafka/proto" + "github.com/stretchr/testify/assert" + "k8s.io/heapster/metrics/core" +) + +type msgProducedToKafka struct { + message string +} + +type fakeKafkaProducer struct { + msgs []msgProducedToKafka +} + +type fakeKafkaSink struct { + core.DataSink + fakeProducer *fakeKafkaProducer +} + +func NewFakeKafkaProducer() *fakeKafkaProducer { + return &fakeKafkaProducer{[]msgProducedToKafka{}} +} + +func (producer *fakeKafkaProducer) Produce(topic string, partition int32, messages ...*proto.Message) (int64, error) { + for _, msg := range messages { + producer.msgs = append(producer.msgs, msgProducedToKafka{string(msg.Value)}) + } + return 0, nil +} + +// Returns a fake kafka sink. +func NewFakeSink() fakeKafkaSink { + producer := NewFakeKafkaProducer() + fakeTimeSeriesTopic := "kafkaTime-test-topic" + return fakeKafkaSink{ + &kafkaSink{ + producer: producer, + dataTopic: fakeTimeSeriesTopic, + }, + producer, + } +} + +func TestStoreDataEmptyInput(t *testing.T) { + fakeSink := NewFakeSink() + dataBatch := core.DataBatch{} + fakeSink.ExportData(&dataBatch) + assert.Equal(t, 0, len(fakeSink.fakeProducer.msgs)) +} + +func TestStoreMultipleDataInput(t *testing.T) { + fakeSink := NewFakeSink() + timestamp := time.Now() + + l := make(map[string]string) + l["namespace_id"] = "123" + l["container_name"] = "/system.slice/-.mount" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l2 := make(map[string]string) + l2["namespace_id"] = "123" + l2["container_name"] = "/system.slice/dbus.service" + l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l3 := make(map[string]string) + l3["namespace_id"] = "123" + l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l4 := make(map[string]string) + l4["namespace_id"] = "" + l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l5 := make(map[string]string) + l5["namespace_id"] = "123" + l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + metricSet1 := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + "/system.slice/-.mount//cpu/limit": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet2 := core.MetricSet{ + Labels: l2, + MetricValues: map[string]core.MetricValue{ + "/system.slice/dbus.service//cpu/usage": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet3 := core.MetricSet{ + Labels: l3, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet4 := core.MetricSet{ + Labels: l4, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet5 := core.MetricSet{ + Labels: l5, + MetricValues: map[string]core.MetricValue{ + "removeme": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + data := core.DataBatch{ + Timestamp: timestamp, + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet1, + "pod2": &metricSet2, + "pod3": &metricSet3, + "pod4": &metricSet4, + "pod5": &metricSet5, + }, + } + + timeStr, err := timestamp.UTC().MarshalJSON() + assert.NoError(t, err) + + fakeSink.ExportData(&data) + + //expect msg string + assert.Equal(t, 5, len(fakeSink.fakeProducer.msgs)) + + var expectMsgTemplate = [5]string{ + `{"MetricsName":"/system.slice/-.mount//cpu/limit","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"container_name":"/system.slice/-.mount","namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"/system.slice/dbus.service//cpu/usage","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"container_name":"/system.slice/dbus.service","namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"test/metric/1","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"test/metric/1","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"namespace_id":"","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"MetricsName":"removeme","MetricsValue":{"value":123456},"MetricsTimestamp":%s,"MetricsTags":{"namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + } + + msgsString := fmt.Sprintf("%s", fakeSink.fakeProducer.msgs) + + for _, mgsTemplate := range expectMsgTemplate { + expectMsg := fmt.Sprintf(mgsTemplate, timeStr) + assert.Contains(t, msgsString, expectMsg) + } + +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/log/log_sink.go b/vendor/k8s.io/heapster/metrics/sinks/log/log_sink.go new file mode 100644 index 0000000000..fec0df5671 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/log/log_sink.go @@ -0,0 +1,119 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logsink + +import ( + "bytes" + "fmt" + "sort" + + "github.com/golang/glog" + "k8s.io/heapster/metrics/core" +) + +type LogSink struct { +} + +func (this *LogSink) Name() string { + return "Log Sink" +} + +func (this *LogSink) Stop() { + // Do nothing. +} + +func batchToString(batch *core.DataBatch) string { + var buffer bytes.Buffer + buffer.WriteString(fmt.Sprintf("DataBatch Timestamp: %s\n\n", batch.Timestamp)) + for _, key := range sortedMetricSetKeys(batch.MetricSets) { + ms := batch.MetricSets[key] + buffer.WriteString(fmt.Sprintf("MetricSet: %s\n", key)) + padding := " " + buffer.WriteString(fmt.Sprintf("%sScrape time: %v %v\n", padding, ms.ScrapeTime, ms.ScrapeTime.UnixNano())) + buffer.WriteString(fmt.Sprintf("%sCreate time: %v %v\n", padding, ms.CreateTime, ms.CreateTime.UnixNano())) + buffer.WriteString(fmt.Sprintf("%sLabels:\n", padding)) + for _, labelName := range sortedLabelKeys(ms.Labels) { + labelValue := ms.Labels[labelName] + buffer.WriteString(fmt.Sprintf("%s%s%s = %s\n", padding, padding, labelName, labelValue)) + } + buffer.WriteString(fmt.Sprintf("%sMetrics:\n", padding)) + for _, metricName := range sortedMetricValueKeys(ms.MetricValues) { + metricValue := ms.MetricValues[metricName] + if core.ValueInt64 == metricValue.ValueType { + buffer.WriteString(fmt.Sprintf("%s%s%s = %d\n", padding, padding, metricName, metricValue.IntValue)) + } else if core.ValueFloat == metricValue.ValueType { + buffer.WriteString(fmt.Sprintf("%s%s%s = %f\n", padding, padding, metricName, metricValue.FloatValue)) + } else { + buffer.WriteString(fmt.Sprintf("%s%s%s = ?\n", padding, padding, metricName)) + } + } + buffer.WriteString(fmt.Sprintf("%sLabeled Metrics:\n", padding)) + for _, metric := range ms.LabeledMetrics { + if core.ValueInt64 == metric.ValueType { + buffer.WriteString(fmt.Sprintf("%s%s%s = %d\n", padding, padding, metric.Name, metric.IntValue)) + } else if core.ValueFloat == metric.ValueType { + buffer.WriteString(fmt.Sprintf("%s%s%s = %f\n", padding, padding, metric.Name, metric.FloatValue)) + } else { + buffer.WriteString(fmt.Sprintf("%s%s%s = ?\n", padding, padding, metric.Name)) + } + for labelName, labelValue := range metric.Labels { + buffer.WriteString(fmt.Sprintf("%s%s%s%s = %s\n", padding, padding, padding, labelName, labelValue)) + } + } + buffer.WriteString("\n") + } + return buffer.String() +} + +func (this *LogSink) ExportData(batch *core.DataBatch) { + glog.Info(batchToString(batch)) +} + +func NewLogSink() *LogSink { + return &LogSink{} +} + +func sortedMetricSetKeys(m map[string]*core.MetricSet) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +func sortedLabelKeys(m map[string]string) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +func sortedMetricValueKeys(m map[string]core.MetricValue) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/log/log_sink_test.go b/vendor/k8s.io/heapster/metrics/sinks/log/log_sink_test.go new file mode 100644 index 0000000000..ff53a68012 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/log/log_sink_test.go @@ -0,0 +1,128 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logsink + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func TestSimpleWrite(t *testing.T) { + now := time.Now() + batch := core.DataBatch{ + Timestamp: now, + MetricSets: make(map[string]*core.MetricSet), + } + batch.MetricSets["pod1"] = &core.MetricSet{ + Labels: map[string]string{"bzium": "hocuspocus"}, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 31415, + }, + }, + LabeledMetrics: []core.LabeledMetric{ + { + Name: "lm", + MetricValue: core.MetricValue{ + MetricType: core.MetricGauge, + ValueType: core.ValueInt64, + IntValue: 279, + }, + Labels: map[string]string{ + "disk": "hard", + }, + }, + }, + } + log := batchToString(&batch) + + assert.True(t, strings.Contains(log, "31415")) + assert.True(t, strings.Contains(log, "m1")) + assert.True(t, strings.Contains(log, "bzium")) + assert.True(t, strings.Contains(log, "hocuspocus")) + assert.True(t, strings.Contains(log, "pod1")) + assert.True(t, strings.Contains(log, "279")) + assert.True(t, strings.Contains(log, "disk")) + assert.True(t, strings.Contains(log, "hard")) + assert.True(t, strings.Contains(log, fmt.Sprintf("%s", now))) +} + +func TestSortedOutput(t *testing.T) { + const ( + label1 = "abcLabel" + label2 = "xyzLabel" + pod1 = "pod1" + pod2 = "pod2" + metric1 = "metricA" + metric2 = "metricB" + ) + metricVal := core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 31415, + } + metricSet := func(pod string) *core.MetricSet { + return &core.MetricSet{ + Labels: map[string]string{label2 + pod: pod, label1 + pod: pod}, + MetricValues: map[string]core.MetricValue{ + metric2 + pod: metricVal, + metric1 + pod: metricVal, + }, + LabeledMetrics: []core.LabeledMetric{}, + } + } + now := time.Now() + batch := core.DataBatch{ + Timestamp: now, + MetricSets: map[string]*core.MetricSet{ + pod2: metricSet(pod2), + pod1: metricSet(pod1), + }, + } + log := batchToString(&batch) + sorted := []string{ + pod1, + label1 + pod1, + label2 + pod1, + metric1 + pod1, + metric2 + pod1, + pod2, + label1 + pod2, + label2 + pod2, + metric1 + pod2, + metric2 + pod2, + } + var ( + previous string + previousIndex int + ) + for _, metric := range sorted { + metricIndex := strings.Index(log, metric) + assert.NotEqual(t, -1, metricIndex, "%q not found", metric) + if previous != "" { + assert.True(t, previousIndex < metricIndex, "%q should be before %q", previous, metric) + } + previous = metric + previousIndex = metricIndex + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/manager.go b/vendor/k8s.io/heapster/metrics/sinks/manager.go new file mode 100644 index 0000000000..a62a990df6 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/manager.go @@ -0,0 +1,159 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sinks + +import ( + "sync" + "time" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/heapster/metrics/core" +) + +const ( + DefaultSinkExportDataTimeout = 20 * time.Second + DefaultSinkStopTimeout = 60 * time.Second +) + +var ( + // Last time Heapster exported data since unix epoch in seconds. + lastExportTimestamp = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "heapster", + Subsystem: "exporter", + Name: "last_time_seconds", + Help: "Last time Heapster exported data since unix epoch in seconds.", + }, + []string{"exporter"}, + ) + + // Time spent exporting data to sink in microseconds. + exporterDuration = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "heapster", + Subsystem: "exporter", + Name: "duration_microseconds", + Help: "Time spent exporting data to sink in microseconds.", + }, + []string{"exporter"}, + ) +) + +func init() { + prometheus.MustRegister(lastExportTimestamp) + prometheus.MustRegister(exporterDuration) +} + +type sinkHolder struct { + sink core.DataSink + dataBatchChannel chan *core.DataBatch + stopChannel chan bool +} + +// Sink Manager - a special sink that distributes data to other sinks. It pushes data +// only to these sinks that completed their previous exports. Data that could not be +// pushed in the defined time is dropped and not retried. +type sinkManager struct { + sinkHolders []sinkHolder + exportDataTimeout time.Duration + stopTimeout time.Duration +} + +func NewDataSinkManager(sinks []core.DataSink, exportDataTimeout, stopTimeout time.Duration) (core.DataSink, error) { + sinkHolders := []sinkHolder{} + for _, sink := range sinks { + sh := sinkHolder{ + sink: sink, + dataBatchChannel: make(chan *core.DataBatch), + stopChannel: make(chan bool), + } + sinkHolders = append(sinkHolders, sh) + go func(sh sinkHolder) { + for { + select { + case data := <-sh.dataBatchChannel: + export(sh.sink, data) + case isStop := <-sh.stopChannel: + glog.V(2).Infof("Stop received: %s", sh.sink.Name()) + if isStop { + sh.sink.Stop() + return + } + } + } + }(sh) + } + return &sinkManager{ + sinkHolders: sinkHolders, + exportDataTimeout: exportDataTimeout, + stopTimeout: stopTimeout, + }, nil +} + +// Guarantees that the export will complete in sinkExportDataTimeout. +func (this *sinkManager) ExportData(data *core.DataBatch) { + var wg sync.WaitGroup + for _, sh := range this.sinkHolders { + wg.Add(1) + go func(sh sinkHolder, wg *sync.WaitGroup) { + defer wg.Done() + glog.V(2).Infof("Pushing data to: %s", sh.sink.Name()) + select { + case sh.dataBatchChannel <- data: + glog.V(2).Infof("Data push completed: %s", sh.sink.Name()) + // everything ok + case <-time.After(this.exportDataTimeout): + glog.Warningf("Failed to push data to sink: %s", sh.sink.Name()) + } + }(sh, &wg) + } + // Wait for all pushes to complete or timeout. + wg.Wait() +} + +func (this *sinkManager) Name() string { + return "Manager" +} + +func (this *sinkManager) Stop() { + for _, sh := range this.sinkHolders { + glog.V(2).Infof("Running stop for: %s", sh.sink.Name()) + + go func(sh sinkHolder) { + select { + case sh.stopChannel <- true: + // everything ok + glog.V(2).Infof("Stop sent to sink: %s", sh.sink.Name()) + + case <-time.After(this.stopTimeout): + glog.Warningf("Failed to stop sink: %s", sh.sink.Name()) + } + return + }(sh) + } +} + +func export(s core.DataSink, data *core.DataBatch) { + startTime := time.Now() + defer lastExportTimestamp. + WithLabelValues(s.Name()). + Set(float64(time.Now().Unix())) + defer exporterDuration. + WithLabelValues(s.Name()). + Observe(float64(time.Since(startTime)) / float64(time.Microsecond)) + + s.ExportData(data) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/manager_test.go b/vendor/k8s.io/heapster/metrics/sinks/manager_test.go new file mode 100644 index 0000000000..931407d513 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/manager_test.go @@ -0,0 +1,131 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sinks + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/util" +) + +func TestAllExportsInTime(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", time.Second) + sink2 := util.NewDummySink("s2", time.Second) + manager, _ := NewDataSinkManager([]core.DataSink{sink1, sink2}, timeout, timeout) + + now := time.Now() + batch := core.DataBatch{ + Timestamp: now, + MetricSets: map[string]*core.MetricSet{}, + } + + manager.ExportData(&batch) + manager.ExportData(&batch) + manager.ExportData(&batch) + + elapsed := time.Now().Sub(now) + if elapsed > 2*timeout+2*time.Second { + t.Fatalf("3xExportData took too long: %s", elapsed) + } + + time.Sleep(time.Second) + assert.Equal(t, 3, sink1.GetExportCount()) + assert.Equal(t, 3, sink2.GetExportCount()) +} + +func TestOneExportInTime(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", time.Second) + sink2 := util.NewDummySink("s2", 30*time.Second) + manager, _ := NewDataSinkManager([]core.DataSink{sink1, sink2}, timeout, timeout) + + now := time.Now() + batch := core.DataBatch{ + Timestamp: now, + MetricSets: map[string]*core.MetricSet{}, + } + + manager.ExportData(&batch) + manager.ExportData(&batch) + manager.ExportData(&batch) + + elapsed := time.Now().Sub(now) + if elapsed > 2*timeout+2*time.Second { + t.Fatalf("3xExportData took too long: %s", elapsed) + } + if elapsed < 2*timeout-1*time.Second { + t.Fatalf("3xExportData took too short: %s", elapsed) + } + + time.Sleep(time.Second) + assert.Equal(t, 3, sink1.GetExportCount()) + assert.Equal(t, 1, sink2.GetExportCount()) +} + +func TestNoExportInTime(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", 30*time.Second) + sink2 := util.NewDummySink("s2", 30*time.Second) + manager, _ := NewDataSinkManager([]core.DataSink{sink1, sink2}, timeout, timeout) + + now := time.Now() + batch := core.DataBatch{ + Timestamp: now, + MetricSets: map[string]*core.MetricSet{}, + } + + manager.ExportData(&batch) + manager.ExportData(&batch) + manager.ExportData(&batch) + + elapsed := time.Now().Sub(now) + if elapsed > 2*timeout+2*time.Second { + t.Fatalf("3xExportData took too long: %s", elapsed) + } + if elapsed < 2*timeout-1*time.Second { + t.Fatalf("3xExportData took too short: %s", elapsed) + } + + time.Sleep(time.Second) + assert.Equal(t, 1, sink1.GetExportCount()) + assert.Equal(t, 1, sink2.GetExportCount()) +} + +func TestStop(t *testing.T) { + timeout := 3 * time.Second + + sink1 := util.NewDummySink("s1", 30*time.Second) + sink2 := util.NewDummySink("s2", 30*time.Second) + manager, _ := NewDataSinkManager([]core.DataSink{sink1, sink2}, timeout, timeout) + + now := time.Now() + manager.Stop() + elapsed := time.Now().Sub(now) + if elapsed > time.Second { + t.Fatalf("stop too long: %s", elapsed) + } + time.Sleep(time.Second) + + assert.Equal(t, true, sink1.IsStopped()) + assert.Equal(t, true, sink2.IsStopped()) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink.go b/vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink.go new file mode 100644 index 0000000000..8eac787612 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink.go @@ -0,0 +1,346 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metricsink + +import ( + "sync" + "time" + + "k8s.io/heapster/metrics/core" +) + +// A simple in-memory storage for metrics. It divides metrics into 2 categories +// * metrics that need to be stored for couple minutes. +// * metrics that need to be stored for longer time (15 min, 1 hour). +// The user of this struct needs to decide what are the long-stored metrics uprfront. +type MetricSink struct { + // Request can come from other threads. + lock sync.Mutex + + // List of metrics that will be stored for up to X seconds. + longStoreMetrics []string + longStoreDuration time.Duration + shortStoreDuration time.Duration + + // Stores full DataBatch with all metrics and labels. + shortStore []*core.DataBatch + // Memory-efficient long/mid term storage for metrics. + longStore []*multimetricStore +} + +// Stores values of a single metrics for different MetricSets. +// Assumes that the user knows what the metric is. +type int64Store map[string]int64 + +type multimetricStore struct { + // Timestamp of the batch from which the metrics were taken. + timestamp time.Time + // Metric name to int64store with metric values. + store map[string]int64Store +} + +func buildMultimetricStore(metrics []string, batch *core.DataBatch) *multimetricStore { + store := multimetricStore{ + timestamp: batch.Timestamp, + store: make(map[string]int64Store, len(metrics)), + } + for _, metric := range metrics { + store.store[metric] = make(int64Store, len(batch.MetricSets)) + } + for key, ms := range batch.MetricSets { + for _, metric := range metrics { + if metricValue, found := ms.MetricValues[metric]; found { + metricstore := store.store[metric] + metricstore[key] = metricValue.IntValue + } + } + } + return &store +} + +func (this *MetricSink) Name() string { + return "Metric Sink" +} + +func (this *MetricSink) Stop() { + // Do nothing. +} + +func (this *MetricSink) ExportData(batch *core.DataBatch) { + this.lock.Lock() + defer this.lock.Unlock() + + now := time.Now() + // TODO: add sorting + this.longStore = append(popOldStore(this.longStore, now.Add(-this.longStoreDuration)), + buildMultimetricStore(this.longStoreMetrics, batch)) + this.shortStore = append(popOld(this.shortStore, now.Add(-this.shortStoreDuration)), batch) +} + +func (this *MetricSink) GetLatestDataBatch() *core.DataBatch { + this.lock.Lock() + defer this.lock.Unlock() + + if len(this.shortStore) == 0 { + return nil + } + return this.shortStore[len(this.shortStore)-1] +} + +func (this *MetricSink) GetShortStore() []*core.DataBatch { + this.lock.Lock() + defer this.lock.Unlock() + + result := make([]*core.DataBatch, 0, len(this.shortStore)) + for _, batch := range this.shortStore { + result = append(result, batch) + } + return result +} + +func (this *MetricSink) GetMetric(metricName string, keys []string, start, end time.Time) map[string][]core.TimestampedMetricValue { + this.lock.Lock() + defer this.lock.Unlock() + + useLongStore := false + for _, longStoreMetric := range this.longStoreMetrics { + if longStoreMetric == metricName { + useLongStore = true + } + } + + result := make(map[string][]core.TimestampedMetricValue) + if useLongStore { + for _, store := range this.longStore { + // Inclusive start and end. + if !store.timestamp.Before(start) && !store.timestamp.After(end) { + substore := store.store[metricName] + for _, key := range keys { + if val, found := substore[key]; found { + result[key] = append(result[key], core.TimestampedMetricValue{ + Timestamp: store.timestamp, + MetricValue: core.MetricValue{ + IntValue: val, + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + }, + }) + } + } + } + } + } else { + for _, batch := range this.shortStore { + // Inclusive start and end. + if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) { + for _, key := range keys { + metricSet, found := batch.MetricSets[key] + if !found { + continue + } + metricValue, found := metricSet.MetricValues[metricName] + if !found { + continue + } + keyResult, found := result[key] + if !found { + keyResult = make([]core.TimestampedMetricValue, 0) + } + keyResult = append(keyResult, core.TimestampedMetricValue{ + Timestamp: batch.Timestamp, + MetricValue: metricValue, + }) + result[key] = keyResult + } + } + } + } + return result +} + +func (this *MetricSink) GetLabeledMetric(metricName string, labels map[string]string, keys []string, start, end time.Time) map[string][]core.TimestampedMetricValue { + // NB: the long store doesn't store labeled metrics, so it's not relevant here + result := make(map[string][]core.TimestampedMetricValue) + for _, batch := range this.shortStore { + // Inclusive start and end + if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) { + for _, key := range keys { + metricSet, found := batch.MetricSets[key] + if !found { + continue + } + + for _, labeledMetric := range metricSet.LabeledMetrics { + if labeledMetric.Name != metricName { + continue + } + + if len(labeledMetric.Labels) != len(labels) { + continue + } + + labelsMatch := true + for k, v := range labels { + if lblMetricVal, ok := labeledMetric.Labels[k]; !ok || lblMetricVal != v { + labelsMatch = false + break + } + } + + if labelsMatch { + result[key] = append(result[key], core.TimestampedMetricValue{ + Timestamp: batch.Timestamp, + MetricValue: labeledMetric.MetricValue, + }) + } + } + } + } + } + + return result +} + +func (this *MetricSink) GetMetricNames(key string) []string { + this.lock.Lock() + defer this.lock.Unlock() + + metricNames := make(map[string]bool) + for _, batch := range this.shortStore { + if set, found := batch.MetricSets[key]; found { + for key := range set.MetricValues { + metricNames[key] = true + } + } + } + result := make([]string, 0, len(metricNames)) + for key := range metricNames { + result = append(result, key) + } + return result +} + +func (this *MetricSink) getAllNames(predicate func(ms *core.MetricSet) bool, + name func(key string, ms *core.MetricSet) string) []string { + this.lock.Lock() + defer this.lock.Unlock() + + if len(this.shortStore) == 0 { + return []string{} + } + + result := make([]string, 0, 0) + for key, value := range this.shortStore[len(this.shortStore)-1].MetricSets { + if predicate(value) { + result = append(result, name(key, value)) + } + } + return result +} + +/* + * For debugging only. + */ +func (this *MetricSink) GetMetricSetKeys() []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { return true }, + func(key string, ms *core.MetricSet) string { return key }) +} + +func (this *MetricSink) GetNodes() []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNode }, + func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelHostname.Key] }) +} + +func (this *MetricSink) GetPods() []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod }, + func(key string, ms *core.MetricSet) string { + return ms.Labels[core.LabelNamespaceName.Key] + "/" + ms.Labels[core.LabelPodName.Key] + }) +} + +func (this *MetricSink) GetNamespaces() []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { + return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNamespace + }, + func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelNamespaceName.Key] }) +} + +func (this *MetricSink) GetPodsFromNamespace(namespace string) []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { + return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod && + ms.Labels[core.LabelNamespaceName.Key] == namespace + }, + func(key string, ms *core.MetricSet) string { + return ms.Labels[core.LabelPodName.Key] + }) +} + +func (this *MetricSink) GetContainersForPodFromNamespace(namespace, pod string) []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { + return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePodContainer && + ms.Labels[core.LabelNamespaceName.Key] == namespace && + ms.Labels[core.LabelPodName.Key] == pod + }, + func(key string, ms *core.MetricSet) string { + return ms.Labels[core.LabelContainerName.Key] + }) +} + +func (this *MetricSink) GetSystemContainersFromNode(node string) []string { + return this.getAllNames( + func(ms *core.MetricSet) bool { + return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeSystemContainer && + ms.Labels[core.LabelHostname.Key] == node + }, + func(key string, ms *core.MetricSet) string { + return ms.Labels[core.LabelContainerName.Key] + }) +} + +func popOld(storage []*core.DataBatch, cutoffTime time.Time) []*core.DataBatch { + result := make([]*core.DataBatch, 0) + for _, batch := range storage { + if batch.Timestamp.After(cutoffTime) { + result = append(result, batch) + } + } + return result +} + +func popOldStore(storages []*multimetricStore, cutoffTime time.Time) []*multimetricStore { + result := make([]*multimetricStore, 0, len(storages)) + for _, store := range storages { + if store.timestamp.After(cutoffTime) { + result = append(result, store) + } + } + return result +} + +func NewMetricSink(shortStoreDuration, longStoreDuration time.Duration, longStoreMetrics []string) *MetricSink { + return &MetricSink{ + longStoreMetrics: longStoreMetrics, + longStoreDuration: longStoreDuration, + shortStoreDuration: shortStoreDuration, + longStore: make([]*multimetricStore, 0), + shortStore: make([]*core.DataBatch, 0), + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink_test.go b/vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink_test.go new file mode 100644 index 0000000000..de5f5a9929 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/metric/metric_sink_test.go @@ -0,0 +1,270 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metricsink + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "k8s.io/heapster/metrics/core" +) + +func makeBatches(now time.Time, key, otherKey string) (core.DataBatch, core.DataBatch, core.DataBatch) { + batch1 := core.DataBatch{ + Timestamp: now.Add(-180 * time.Second), + MetricSets: map[string]*core.MetricSet{ + key: { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodNamespace.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 60, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 666, + }, + }, + }, + }, + } + + batch2 := core.DataBatch{ + Timestamp: now.Add(-60 * time.Second), + MetricSets: map[string]*core.MetricSet{ + key: { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodNamespace.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 40, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 444, + }, + }, + LabeledMetrics: []core.LabeledMetric{ + { + Name: "somelblmetric", + Labels: map[string]string{"lbl1": "val1.1", "lbl2": "val2.1"}, + MetricValue: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 8675, + }, + }, + { + Name: "otherlblmetric", + Labels: map[string]string{"lbl1": "val1.1", "lbl2": "val2.1"}, + MetricValue: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 1234, + }, + }, + }, + }, + }, + } + + batch3 := core.DataBatch{ + Timestamp: now.Add(-20 * time.Second), + MetricSets: map[string]*core.MetricSet{ + key: { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodNamespace.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 20, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 222, + }, + }, + LabeledMetrics: []core.LabeledMetric{ + { + Name: "somelblmetric", + Labels: map[string]string{"lbl1": "val1.1", "lbl2": "val2.1"}, + MetricValue: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 309, + }, + }, + { + Name: "somelblmetric", + Labels: map[string]string{"lbl1": "val1.2", "lbl2": "val2.1"}, + MetricValue: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 5678, + }, + }, + }, + }, + otherKey: { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodNamespace.Key: "ns1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 123, + }, + }, + }, + }, + } + + return batch1, batch2, batch3 +} + +func TestGetMetrics(t *testing.T) { + now := time.Now() + key := core.PodKey("ns1", "pod1") + otherKey := core.PodKey("ns1", "other") + + batch1, batch2, batch3 := makeBatches(now, key, otherKey) + + metrics := NewMetricSink(45*time.Second, 120*time.Second, []string{"m1"}) + metrics.ExportData(&batch1) + metrics.ExportData(&batch2) + metrics.ExportData(&batch3) + + //batch1 is discarded by long store + result1 := metrics.GetMetric("m1", []string{key}, now.Add(-120*time.Second), now) + assert.Equal(t, 2, len(result1[key])) + assert.Equal(t, 40, result1[key][0].MetricValue.IntValue) + assert.Equal(t, 20, result1[key][1].MetricValue.IntValue) + assert.Equal(t, 1, len(metrics.GetMetric("m1", []string{otherKey}, now.Add(-120*time.Second), now)[otherKey])) + + //batch1 is discarded by long store and batch2 doesn't belong to time window + assert.Equal(t, 1, len(metrics.GetMetric("m1", []string{key}, now.Add(-30*time.Second), now)[key])) + + //batch1 and batch1 are discarded by short store + assert.Equal(t, 1, len(metrics.GetMetric("m2", []string{key}, now.Add(-120*time.Second), now)[key])) + + //nothing is in time window + assert.Equal(t, 0, len(metrics.GetMetric("m2", []string{key}, now.Add(-10*time.Second), now)[key])) + + metricNames := metrics.GetMetricNames(key) + assert.Equal(t, 2, len(metricNames)) + assert.Contains(t, metricNames, "m1") + assert.Contains(t, metricNames, "m2") +} + +func TestGetLabeledMetrics(t *testing.T) { + now := time.Now().UTC() + key := core.PodKey("ns1", "pod1") + otherKey := core.PodKey("ns1", "other") + + batch1, batch2, batch3 := makeBatches(now, key, otherKey) + + metrics := NewMetricSink(45*time.Second, 120*time.Second, []string{"m1"}) + metrics.ExportData(&batch1) + metrics.ExportData(&batch2) + metrics.ExportData(&batch3) + + result := metrics.GetLabeledMetric("somelblmetric", map[string]string{"lbl1": "val1.1", "lbl2": "val2.1"}, []string{key}, now.Add(-120*time.Second), now) + + assert.Equal(t, []core.TimestampedMetricValue{ + { + Timestamp: now.Add(-20 * time.Second), + MetricValue: core.MetricValue{ + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 309, + }, + }, + }, result[key]) +} + +func TestGetNames(t *testing.T) { + now := time.Now() + key := core.PodKey("ns1", "pod1") + otherKey := core.PodKey("ns1", "other") + + batch := core.DataBatch{ + Timestamp: now.Add(-20 * time.Second), + MetricSets: map[string]*core.MetricSet{ + key: { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodNamespace.Key: "ns1", + core.LabelNamespaceName.Key: "ns1", + core.LabelPodName.Key: "pod1", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 20, + }, + "m2": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 222, + }, + }, + }, + otherKey: { + Labels: map[string]string{ + core.LabelMetricSetType.Key: core.MetricSetTypePod, + core.LabelPodNamespace.Key: "ns2", + core.LabelNamespaceName.Key: "ns2", + core.LabelPodName.Key: "pod2", + }, + MetricValues: map[string]core.MetricValue{ + "m1": { + ValueType: core.ValueInt64, + MetricType: core.MetricGauge, + IntValue: 123, + }, + }, + }, + }, + } + + metrics := NewMetricSink(45*time.Second, 120*time.Second, []string{"m1"}) + metrics.ExportData(&batch) + + assert.Contains(t, metrics.GetPods(), "ns1/pod1") + assert.Contains(t, metrics.GetPods(), "ns2/pod2") + assert.Contains(t, metrics.GetPodsFromNamespace("ns1"), "pod1") + assert.NotContains(t, metrics.GetPodsFromNamespace("ns1"), "pod2") + assert.Contains(t, metrics.GetMetricSetKeys(), key) + assert.Contains(t, metrics.GetMetricSetKeys(), otherKey) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/config.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/config.go new file mode 100644 index 0000000000..faa4f460d6 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/config.go @@ -0,0 +1,60 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "net/url" + + "github.com/rackspace/gophercloud" +) + +// Config represents the configuration of the Monasca sink. +type Config struct { + gophercloud.AuthOptions + MonascaURL string +} + +// NewConfig builds a configuration object from the parameters of the monasca sink. +func NewConfig(opts url.Values) Config { + config := Config{} + if len(opts["keystone-url"]) >= 1 { + config.IdentityEndpoint = opts["keystone-url"][0] + } + if len(opts["tenant-id"]) >= 1 { + config.TenantID = opts["tenant-id"][0] + } + if len(opts["username"]) >= 1 { + config.Username = opts["username"][0] + } + if len(opts["user-id"]) >= 1 { + config.UserID = opts["user-id"][0] + } + if len(opts["password"]) >= 1 { + config.Password = opts["password"][0] + } + if len(opts["api-key"]) >= 1 { + config.APIKey = opts["api-key"][0] + } + if len(opts["domain-id"]) >= 1 { + config.DomainID = opts["domain-id"][0] + } + if len(opts["domain-name"]) >= 1 { + config.DomainName = opts["domain-name"][0] + } + if len(opts["monasca-url"]) >= 1 { + config.MonascaURL = opts["monasca-url"][0] + } + return config +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/data_test.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/data_test.go new file mode 100644 index 0000000000..78832db871 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/data_test.go @@ -0,0 +1,226 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "time" + + "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" + "k8s.io/heapster/metrics/core" +) + +var measureTime = time.Now() + +// common labels: +var testInput = &core.DataBatch{ + Timestamp: measureTime, + MetricSets: map[string]*core.MetricSet{ + "set1": &core.MetricSet{ + MetricValues: map[string]core.MetricValue{ + "m2": core.MetricValue{ValueType: core.ValueInt64, IntValue: 2 ^ 63}, + "m3": core.MetricValue{ValueType: core.ValueFloat, FloatValue: -1023.0233}, + }, + Labels: map[string]string{ + core.LabelHostname.Key: "h1", + }, + LabeledMetrics: []core.LabeledMetric{}, + }, + "set2": &core.MetricSet{ + MetricValues: map[string]core.MetricValue{}, + Labels: map[string]string{ + core.LabelHostname.Key: "10.140.32.11", + }, + LabeledMetrics: []core.LabeledMetric{ + core.LabeledMetric{ + Name: "cpu/usage", + Labels: map[string]string{ + core.LabelContainerName.Key: "POD", + core.LabelPodName.Key: "mypod-hc3s", + core.LabelLabels.Key: "run:test,pod.name:default/test-u2dc", + core.LabelHostID.Key: "", + }, + MetricValue: core.MetricValue{ + ValueType: core.ValueInt64, + IntValue: 1, + }, + }, + core.LabeledMetric{ + Name: "memory/usage", + Labels: map[string]string{ + core.LabelContainerName.Key: "machine", + core.LabelLabels.Key: "pod.name:default/test-u2dc,run:test2,foo:bar", + core.LabelHostID.Key: "myhost", + }, + MetricValue: core.MetricValue{ + ValueType: core.ValueFloat, + FloatValue: 64.0, + }, + }, + }, + }, + }, +} + +var expectedTransformed = []metric{ + metric{ + Name: "m2", + Dimensions: map[string]string{ + "component": emptyValue, + "hostname": "h1", + "service": "kubernetes", + core.LabelContainerName.Key: emptyValue, + }, + Value: 2 ^ 63, + Timestamp: measureTime.UnixNano() / 1000000, + ValueMeta: map[string]string{}, + }, + metric{ + Name: "m3", + Dimensions: map[string]string{ + "component": emptyValue, + "hostname": "h1", + "service": "kubernetes", + core.LabelContainerName.Key: emptyValue, + }, + Value: float64(float32(-1023.0233)), + Timestamp: measureTime.UnixNano() / 1000000, + ValueMeta: map[string]string{}, + }, + metric{ + Name: "cpu.usage", + Dimensions: map[string]string{ + "component": "mypod-hc3s", + "hostname": "10.140.32.11", + "service": "kubernetes", + core.LabelContainerName.Key: "POD", + }, + Value: 1.0, + Timestamp: measureTime.UnixNano() / 1000000, + ValueMeta: map[string]string{ + core.LabelLabels.Key: "run:test pod.name:default/test-u2dc", + }, + }, + metric{ + Name: "memory.usage", + Dimensions: map[string]string{ + "component": emptyValue, + "hostname": "10.140.32.11", + "service": "kubernetes", + core.LabelContainerName.Key: "machine", + }, + Value: float64(float32(64.0)), + Timestamp: measureTime.UnixNano() / 1000000, + ValueMeta: map[string]string{ + core.LabelLabels.Key: "pod.name:default/test-u2dc run:test2 foo:bar", + core.LabelHostID.Key: "myhost", + }, + }, +} + +const testToken = "e80b74" + +var invalidToken = &tokens.Token{ID: "invalidToken", ExpiresAt: time.Unix(time.Now().Unix()-5000, 0)} +var validToken = &tokens.Token{ID: testToken, ExpiresAt: time.Unix(time.Now().Unix()+50000, 0)} + +var testConfig = Config{} + +const ( + testUsername = "Joe" + testPassword = "bar" + testUserID = "0ca8f6" + testDomainID = "1789d1" + testDomainName = "example.com" +) + +var ( + ksVersionResp string + ksAuthResp string + ksServicesResp string + ksEndpointsResp string + monUnauthorizedResp string + monEmptyDimResp string +) + +func initKeystoneRespStubs() { + ksVersionResp = `{ + "versions": { + "values": [{ + "status": "stable", + "updated": "2015-03-30T00:00:00Z", + "id": "v3.4", + "links": [{ + "href": "` + keystoneAPIStub.URL + `", + "rel": "self" + }] + }] + } + }` + ksAuthResp = `{ + "token": { + "audit_ids": ["VcxU2JYqT8OzfUVvrjEITQ", "qNUTIJntTzO1-XUk5STybw"], + "expires_at": "2013-02-27T18:30:59.999999Z", + "issued_at": "2013-02-27T16:30:59.999999Z", + "methods": [ + "password" + ], + "user": { + "domain": { + "id": "1789d1", + "name": "example.com" + }, + "id": "0ca8f6", + "name": "Joe" + } + } + }` + ksServicesResp = `{ + "services": [{ + "description": "Monasca Service", + "id": "ee057c", + "links": { + "self": "` + keystoneAPIStub.URL + `/v3/services/ee057c" + }, + "name": "Monasca", + "type": "monitoring" + }], + "links": { + "self": "` + keystoneAPIStub.URL + `/v3/services", + "previous": null, + "next": null + } + }` + ksEndpointsResp = `{ + "endpoints": [ + { + "enabled": true, + "id": "6fedc0", + "interface": "public", + "links": { + "self": "` + keystoneAPIStub.URL + `/v3/endpoints/6fedc0" + }, + "region_id": "us-east-1", + "service_id": "ee057c", + "url": "` + monascaAPIStub.URL + `" + } + ], + "links": { + "self": "` + keystoneAPIStub.URL + `/v3/endpoints", + "previous": null, + "next": null + } + }` + monUnauthorizedResp = "Invaild token provided" + monEmptyDimResp = "Empty dimension detected" +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/driver.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/driver.go new file mode 100644 index 0000000000..6d2f7512fa --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/driver.go @@ -0,0 +1,195 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "fmt" + "net/http" + "net/url" + "reflect" + "strings" + "sync" + "time" + + "github.com/golang/glog" + "k8s.io/heapster/metrics/core" +) + +type monascaSink struct { + client Client + sync.RWMutex + numberOfFailures int +} + +// Pushes the specified metric measurement to the Monasca API. +// The Timeseries are transformed to monasca metrics beforehand. +// Timeseries that cannot be translated to monasca metrics are skipped. +func (sink *monascaSink) ExportData(dataBatch *core.DataBatch) { + sink.Lock() + defer sink.Unlock() + + metrics := sink.processMetrics(dataBatch) + code, response, err := sink.client.SendRequest("POST", "/metrics", metrics) + if err != nil { + glog.Errorf("%s", err) + sink.numberOfFailures++ + return + } + if code != http.StatusNoContent { + glog.Error(response) + sink.numberOfFailures++ + } +} + +// a monasca metric definition +type metric struct { + Name string `json:"name"` + Dimensions map[string]string `json:"dimensions"` + Timestamp int64 `json:"timestamp"` + Value float64 `json:"value"` + ValueMeta map[string]string `json:"value_meta"` +} + +func (sink *monascaSink) processMetrics(dataBatch *core.DataBatch) []metric { + metrics := []metric{} + for _, metricSet := range dataBatch.MetricSets { + m := sink.processMetricSet(dataBatch, metricSet) + metrics = append(metrics, m...) + } + return metrics +} + +func (sink *monascaSink) processMetricSet(dataBatch *core.DataBatch, metricSet *core.MetricSet) []metric { + metrics := []metric{} + + // process unlabeled metrics + for metricName, metricValue := range metricSet.MetricValues { + m := sink.processMetric(metricSet.Labels, metricName, dataBatch.Timestamp, metricValue.GetValue()) + if nil != m { + metrics = append(metrics, *m) + } + } + + // process labeled metrics + for _, metric := range metricSet.LabeledMetrics { + labels := map[string]string{} + for k, v := range metricSet.Labels { + labels[k] = v + } + for k, v := range metric.Labels { + labels[k] = v + } + m := sink.processMetric(labels, metric.Name, dataBatch.Timestamp, metric.GetValue()) + if nil != m { + metrics = append(metrics, *m) + } + } + return metrics +} + +func (sink *monascaSink) processMetric(labels map[string]string, name string, timestamp time.Time, value interface{}) *metric { + val, err := sink.convertValue(value) + if err != nil { + glog.Warningf("Metric cannot be pushed to monasca. %#v", value) + return nil + } + dims, valueMeta := sink.processLabels(labels) + m := metric{ + Name: strings.Replace(name, "/", ".", -1), + Dimensions: dims, + Timestamp: (timestamp.UnixNano() / 1000000), + Value: val, + ValueMeta: valueMeta, + } + return &m +} + +// convert the Timeseries value to a monasca value +func (sink *monascaSink) convertValue(val interface{}) (float64, error) { + switch val.(type) { + case int: + return float64(val.(int)), nil + case int64: + return float64(val.(int64)), nil + case bool: + if val.(bool) { + return 1.0, nil + } + return 0.0, nil + case float32: + return float64(val.(float32)), nil + case float64: + return val.(float64), nil + } + return 0.0, fmt.Errorf("Unsupported monasca metric value type %T", reflect.TypeOf(val)) +} + +const ( + emptyValue = "none" + monascaComponent = "component" + monascaService = "service" + monascaHostname = "hostname" +) + +// preprocesses heapster labels, splitting into monasca dimensions and monasca meta-values +func (sink *monascaSink) processLabels(labels map[string]string) (map[string]string, map[string]string) { + dims := map[string]string{} + valueMeta := map[string]string{} + + // labels to dimensions + dims[monascaComponent] = sink.processDimension(labels[core.LabelPodName.Key]) + dims[monascaHostname] = sink.processDimension(labels[core.LabelHostname.Key]) + dims[core.LabelContainerName.Key] = sink.processDimension(labels[core.LabelContainerName.Key]) + dims[monascaService] = "kubernetes" + + // labels to valueMeta + for i, v := range labels { + if i != core.LabelPodName.Key && i != core.LabelHostname.Key && + i != core.LabelContainerName.Key && v != "" { + valueMeta[i] = strings.Replace(v, ",", " ", -1) + } + } + return dims, valueMeta +} + +// creates a valid dimension value +func (sink *monascaSink) processDimension(value string) string { + if value != "" { + v := strings.Replace(value, "/", ".", -1) + return strings.Replace(v, ",", " ", -1) + } + return emptyValue +} + +func (sink *monascaSink) Name() string { + return "Monasca Sink" +} + +func (sink *monascaSink) Stop() { + // Nothing needs to be done +} + +// CreateMonascaSink creates a monasca sink that can consume the Monasca APIs to create metrics. +func CreateMonascaSink(uri *url.URL) (core.DataSink, error) { + opts := uri.Query() + config := NewConfig(opts) + client, err := NewMonascaClient(config) + if err != nil { + return nil, err + } + monascaSink := monascaSink{client: client} + glog.Infof("Created Monasca sink. Monasca server running on: %s", client.GetURL().String()) + return &monascaSink, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/driver_test.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/driver_test.go new file mode 100644 index 0000000000..80598efa5d --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/driver_test.go @@ -0,0 +1,147 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +// test the transformation of timeseries to monasca metrics +func TestTimeseriesTransform(t *testing.T) { + // setup + sut := monascaSink{} + + // do + metrics := sut.processMetrics(testInput) + + // assert + set1 := map[string]metric{} + set2 := map[string]metric{} + for _, m := range expectedTransformed { + set1[m.Name] = m + } + for _, m := range metrics { + set2[m.Name] = m + } + assert.Equal(t, set1, set2) +} + +// test if the sink creation fails when password is not provided +func TestMissingPasswordError(t *testing.T) { + // setup + uri, _ := url.Parse("monasca:?keystone-url=" + testConfig.IdentityEndpoint + "&user_id=" + testUserID) + + // do + _, err := CreateMonascaSink(uri) + + // assert + assert.Error(t, err) +} + +// test if the sink creation fails when keystone-url is not provided +func TestMissingKeystoneURLError(t *testing.T) { + // setup + uri, _ := url.Parse("monasca:?user_id=" + testUserID + "&password=" + testPassword) + + // do + _, err := CreateMonascaSink(uri) + + // assert + assert.Error(t, err) +} + +// test if the sink creation fails when neither user-id nor username are provided +func TestMissingUserError(t *testing.T) { + // setup + uri, _ := url.Parse("monasca:?keystone-url=" + testConfig.IdentityEndpoint + "&password=" + testPassword) + + // do + _, err := CreateMonascaSink(uri) + + // assert + assert.Error(t, err) +} + +// test if the sink creation fails when domain_id and domainname are missing +// and username is provided +func TestMissingDomainWhenUsernameError(t *testing.T) { + // setup + uri, _ := url.Parse("monasca:?keystone-url=" + testConfig.IdentityEndpoint + "&password=" + + testPassword + "&username=" + testUsername) + + // do + _, err := CreateMonascaSink(uri) + + // assert + assert.Error(t, err) +} + +// test if the sink creation fails when password is not provided +func TestWrongMonascaURLError(t *testing.T) { + // setup + uri, _ := url.Parse("monasca:?keystone-url=" + testConfig.IdentityEndpoint + "&password=" + + testConfig.Password + "&user-id=" + testConfig.UserID + "&monasca-url=_malformed") + + // do + _, err := CreateMonascaSink(uri) + + // assert + assert.Error(t, err) +} + +// test the successful creation of the monasca +func TestMonascaSinkCreation(t *testing.T) { + // setup + uri, _ := url.Parse("monasca:?keystone-url=" + testConfig.IdentityEndpoint + "&password=" + + testConfig.Password + "&user-id=" + testConfig.UserID) + + // do + _, err := CreateMonascaSink(uri) + + // assert + assert.NoError(t, err) +} + +// integration test of storing metrics +func TestStoreMetrics(t *testing.T) { + // setup + ks, _ := NewKeystoneClient(testConfig) + monURL, err := ks.MonascaURL() + assert.NoError(t, err) + sut := monascaSink{client: &ClientImpl{ksClient: ks, monascaURL: monURL}} + + // do + sut.ExportData(testInput) + + // assert + assert.Equal(t, 0, sut.numberOfFailures) +} + +// integration test of failure to create metrics +func TestStoreMetricsFailure(t *testing.T) { + // setup + ks, _ := NewKeystoneClient(testConfig) + monURL, _ := url.Parse("http://unexisting.monasca.com") + sut := monascaSink{client: &ClientImpl{ksClient: ks, monascaURL: monURL}} + + // do + sut.ExportData(testInput) + + // assert + assert.Equal(t, 1, sut.numberOfFailures) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/init_test.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/init_test.go new file mode 100644 index 0000000000..e649111572 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/init_test.go @@ -0,0 +1,195 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "os" + "sync" + "testing" + + "github.com/stretchr/testify/mock" +) + +type keystoneClientMock struct { + *KeystoneClientImpl + mock.Mock +} + +func (ks *keystoneClientMock) MonascaURL() (*url.URL, error) { + args := ks.Called() + return args.Get(0).(*url.URL), args.Error(1) +} + +func (ks *keystoneClientMock) GetToken() (string, error) { + args := ks.Called() + return args.String(0), args.Error(1) +} + +var monascaAPIStub *httptest.Server +var keystoneAPIStub *httptest.Server + +type ksAuthRequest struct { + Auth ksAuth `json:"auth"` +} + +type ksAuth struct { + Identity ksIdentity `json:"identity"` +} + +type ksIdentity struct { + Methods []string `json:"methods"` + Password ksPassword `json:"password"` +} + +type ksPassword struct { + User ksUser `json:"user"` +} + +type ksUser struct { + ID string `json:"id"` + Password string `json:"password"` +} + +// prepare before testing +func TestMain(m *testing.M) { + // monasca stub + monascaMutex := &sync.Mutex{} + defer monascaAPIStub.Close() + monascaAPIStub = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + monascaMutex.Lock() + defer monascaMutex.Unlock() + w.Header().Set("Content-Type", "application/json") + switch r.URL.Path { + case "/metrics": + defer r.Body.Close() + contents, err := ioutil.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("%s", err))) + break + } + + // umarshal & do type checking on the fly + metrics := []metric{} + err = json.Unmarshal(contents, &metrics) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("%s", err))) + break + } + + // check for empty dimensions + for _, metric := range metrics { + for _, dimVal := range metric.Dimensions { + if dimVal == "" { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(monEmptyDimResp)) + break + } + } + } + + // check token + token := r.Header.Get("X-Auth-Token") + if token != testToken { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte(monUnauthorizedResp)) + break + } + w.WriteHeader(http.StatusNoContent) + break + case "/versions": + w.WriteHeader(http.StatusOK) + break + } + })) + + // keystone stub + keystoneMutex := &sync.Mutex{} + defer keystoneAPIStub.Close() + keystoneAPIStub = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + keystoneMutex.Lock() + defer keystoneMutex.Unlock() + w.Header().Add("Content-Type", "application/json") + switch r.URL.Path { + case "/": + w.Write([]byte(ksVersionResp)) + break + case "/v3/auth/tokens": + if r.Method == "HEAD" { + ksToken := r.Header.Get("X-Auth-Token") + if ksToken != testToken { + w.WriteHeader(http.StatusUnauthorized) + break + } + token := r.Header.Get("X-Subject-Token") + if token == testToken { + // token valid + w.WriteHeader(http.StatusNoContent) + break + } + // token invalid + w.WriteHeader(http.StatusNotFound) + break + } + + // read request + defer r.Body.Close() + contents, err := ioutil.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + req := ksAuthRequest{} + err = json.Unmarshal(contents, &req) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + // authenticate + if req.Auth.Identity.Password.User.ID != testConfig.UserID || + req.Auth.Identity.Password.User.Password != testConfig.Password { + w.WriteHeader(http.StatusUnauthorized) + return + } + + // return a token + w.Header().Add("X-Subject-Token", testToken) + w.WriteHeader(http.StatusAccepted) + w.Write([]byte(ksAuthResp)) + break + case "/v3/services": + w.Write([]byte(ksServicesResp)) + break + case "/v3/endpoints": + w.Write([]byte(ksEndpointsResp)) + default: + w.WriteHeader(http.StatusInternalServerError) + } + })) + initKeystoneRespStubs() + + testConfig.Password = "bar" + testConfig.UserID = "0ca8f6" + testConfig.IdentityEndpoint = keystoneAPIStub.URL + "/v3" + os.Exit(m.Run()) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client.go new file mode 100644 index 0000000000..fd4e6ab44d --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client.go @@ -0,0 +1,147 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "net/url" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" + "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints" + "github.com/rackspace/gophercloud/openstack/identity/v3/services" + "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" + "github.com/rackspace/gophercloud/pagination" +) + +// KeystoneClient defines the interface of any client that can talk with Keystone. +type KeystoneClient interface { + MonascaURL() (*url.URL, error) + GetToken() (string, error) +} + +// KeystoneClientImpl can authenticate with keystone and provide +// tokens, required for accessing the Monasca APIs. +type KeystoneClientImpl struct { + client *gophercloud.ServiceClient + opts gophercloud.AuthOptions + token *tokens.Token + monascaURL *url.URL +} + +// MonascaURL Discovers the monasca service API endpoint and returns it. +func (ksClient *KeystoneClientImpl) MonascaURL() (*url.URL, error) { + if ksClient.monascaURL == nil { + monascaURL, err := ksClient.serviceEndpoint("monitoring", gophercloud.AvailabilityPublic) + if err != nil { + return nil, err + } + ksClient.monascaURL = monascaURL + } + return ksClient.monascaURL, nil +} + +// discovers a single service endpoint from a given service type +func (ksClient *KeystoneClientImpl) serviceEndpoint(serviceType string, availability gophercloud.Availability) (*url.URL, error) { + serviceID, err := ksClient.serviceID(serviceType) + if err != nil { + return nil, err + } + return ksClient.endpointURL(serviceID, availability) +} + +// finds the URL for a given service ID and availability +func (ksClient *KeystoneClientImpl) endpointURL(serviceID string, availability gophercloud.Availability) (*url.URL, error) { + opts := endpoints.ListOpts{Availability: availability, ServiceID: serviceID, PerPage: 1, Page: 1} + pager := endpoints.List(ksClient.client, opts) + endpointURL := (*url.URL)(nil) + err := pager.EachPage(func(page pagination.Page) (bool, error) { + endpointList, err := endpoints.ExtractEndpoints(page) + if err != nil { + return false, err + } + for _, e := range endpointList { + URL, err := url.Parse(e.URL) + if err != nil { + return false, err + } + endpointURL = URL + } + return false, nil + }) + if err != nil { + return nil, err + } + return endpointURL, nil +} + +// returns the first found service ID from a given service type +func (ksClient *KeystoneClientImpl) serviceID(serviceType string) (string, error) { + opts := services.ListOpts{ServiceType: serviceType, PerPage: 1, Page: 1} + pager := services.List(ksClient.client, opts) + serviceID := "" + err := pager.EachPage(func(page pagination.Page) (bool, error) { + serviceList, err := services.ExtractServices(page) + if err != nil { + return false, err + } + for _, s := range serviceList { + serviceID = s.ID + } + return false, nil + }) + if err != nil { + return "", err + } + return serviceID, nil +} + +// GetToken returns a valid X-Auth-Token. +func (ksClient *KeystoneClientImpl) GetToken() (string, error) { + // generate if needed + if ksClient.token == nil { + return ksClient.newToken() + } + // validate + valid, err := tokens.Validate(ksClient.client, ksClient.token.ID) + if err != nil || !valid { + return ksClient.newToken() + } + return ksClient.token.ID, nil +} + +// generates a brand new Keystone token +func (ksClient *KeystoneClientImpl) newToken() (string, error) { + token, err := tokens.Create(ksClient.client, ksClient.opts, nil).Extract() + if err != nil { + return "", err + } + ksClient.token = token + return token.ID, nil +} + +// NewKeystoneClient initilizes a keystone client with the provided configuration. +func NewKeystoneClient(config Config) (KeystoneClient, error) { + opts := config.AuthOptions + provider, err := openstack.AuthenticatedClient(opts) + if err != nil { + return nil, err + } + client := openstack.NewIdentityV3(provider) + // build a closure for ksClient reauthentication + client.ReauthFunc = func() error { + return openstack.AuthenticateV3(client.ProviderClient, opts) + } + return &KeystoneClientImpl{client: client, opts: opts, token: nil, monascaURL: nil}, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client_test.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client_test.go new file mode 100644 index 0000000000..5255fd787d --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/keystone_client_test.go @@ -0,0 +1,98 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "testing" + + "github.com/rackspace/gophercloud/openstack" + "github.com/stretchr/testify/assert" +) + +func TestMonascaURLDiscovery(t *testing.T) { + // setup + ksClient, err := NewKeystoneClient(testConfig) + assert.NoError(t, err) + + // do + monURL, err := ksClient.MonascaURL() + + // assert + assert.NoError(t, err) + assert.Equal(t, monURL.String(), monascaAPIStub.URL) +} + +func TestGetTokenWhenMissing(t *testing.T) { + // setup + ksClient, err := NewKeystoneClient(testConfig) + assert.NoError(t, err) + + // do + token, err := ksClient.GetToken() + + // assert + assert.NoError(t, err) + assert.Equal(t, token, testToken) +} + +func TestGetTokenWhenInvalid(t *testing.T) { + // setup + opts := testConfig.AuthOptions + provider, err := openstack.AuthenticatedClient(opts) + assert.NoError(t, err) + client := openstack.NewIdentityV3(provider) + ksClient := &KeystoneClientImpl{client: client, opts: opts, token: invalidToken, monascaURL: nil} + + // do + token, err := ksClient.GetToken() + + // assert + assert.NoError(t, err) + assert.Equal(t, token, testToken) +} + +func TestGetTokenWhenValid(t *testing.T) { + // setup + opts := testConfig.AuthOptions + provider, err := openstack.AuthenticatedClient(opts) + assert.NoError(t, err) + client := openstack.NewIdentityV3(provider) + ksClient := &KeystoneClientImpl{client: client, opts: opts, token: validToken, monascaURL: nil} + + // do + token, err := ksClient.GetToken() + + // assert + assert.NoError(t, err) + assert.Equal(t, token, testToken) +} + +func TestKeystoneClientReauthenticate(t *testing.T) { + // setup + opts := testConfig.AuthOptions + provider, err := openstack.AuthenticatedClient(opts) + assert.NoError(t, err) + client := openstack.NewIdentityV3(provider) + client.TokenID = "someinvalidtoken" + client.ReauthFunc = func() error { return openstack.AuthenticateV3(client.ProviderClient, opts) } + ksClient := &KeystoneClientImpl{client: client, opts: opts, token: validToken, monascaURL: nil} + + // do + token, err := ksClient.GetToken() + + // assert + assert.NoError(t, err) + assert.Equal(t, token, testToken) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client.go new file mode 100644 index 0000000000..1818ad17a8 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client.go @@ -0,0 +1,139 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/golang/glog" +) + +// Client specifies the methods of any client ot the Monasca API +type Client interface { + SendRequest(method, path string, request interface{}) (int, string, error) + GetURL() *url.URL + CheckHealth() bool +} + +// ClientImpl implements the monasca API client interface. +type ClientImpl struct { + ksClient KeystoneClient + monascaURL *url.URL +} + +// SendRequest to the Monasca API, authenticating on the fly. +// Returns 0, "", err if the request cannot be built. +// Returns statusCode, response, nil if communication with the server was OK. +func (monClient *ClientImpl) SendRequest(method, path string, request interface{}) (int, string, error) { + req, err := monClient.prepareRequest(method, path, request) + if err != nil { + return 0, "", err + } + statusCode, response, err := monClient.receiveResponse(req) + if err != nil { + return 0, "", err + } + return statusCode, response, nil +} + +func (monClient *ClientImpl) prepareRequest(method string, path string, request interface{}) (*http.Request, error) { + // authenticate + token, err := monClient.ksClient.GetToken() + if err != nil { + return nil, err + } + // marshal + var rawRequest io.Reader + if request != nil { + jsonRequest, err := json.Marshal(request) + if err != nil { + return nil, err + } + rawRequest = bytes.NewReader(jsonRequest) + } + // build request + req, err := http.NewRequest(method, monClient.GetURL().String()+path, rawRequest) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("X-Auth-Token", token) + return req, nil +} + +func (monClient *ClientImpl) receiveResponse(req *http.Request) (int, string, error) { + resp, err := (&http.Client{}).Do(req) + if err != nil { + return 0, "", err + } + defer resp.Body.Close() + contents, err := ioutil.ReadAll(resp.Body) + respString := "" + if err != nil { + glog.Warning("Cannot read monasca API's response.") + respString = "Cannot read monasca API's response." + } else { + respString = string(contents) + } + return resp.StatusCode, fmt.Sprintf("%s", respString), nil +} + +// GetURL of the Monasca API server. +func (monClient *ClientImpl) GetURL() *url.URL { + return monClient.monascaURL +} + +// CheckHealth of the monasca API server. +func (monClient *ClientImpl) CheckHealth() bool { + code, _, _ := monClient.SendRequest("GET", "/", nil) + return code == http.StatusOK +} + +// NewMonascaClient creates a monasca client. +func NewMonascaClient(config Config) (Client, error) { + // create keystone client + ksClient, err := NewKeystoneClient(config) + if err != nil { + return nil, err + } + + // detect monasca URL + monascaURL := (*url.URL)(nil) + if config.MonascaURL != "" { + monascaURL, err = url.Parse(config.MonascaURL) + if err != nil { + return nil, fmt.Errorf("Malformed monasca-url sink parameter. %s", err) + } + } else { + monascaURL, err = ksClient.MonascaURL() + if err != nil { + return nil, fmt.Errorf("Failed to automatically detect Monasca service: %s", err) + } + } + + // create monasca client + client := &ClientImpl{ksClient: ksClient, monascaURL: monascaURL} + healthy := client.CheckHealth() + if !healthy { + return nil, fmt.Errorf("Monasca is not running on the provided/discovered URL: %s", client.GetURL().String()) + } + return client, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client_test.go b/vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client_test.go new file mode 100644 index 0000000000..316034e89f --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/monasca/monasca_client_test.go @@ -0,0 +1,119 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package monasca + +import ( + "net/http" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func initClientSUT() (*keystoneClientMock, Client) { + ksClientMock := new(keystoneClientMock) + monURL, _ := url.Parse(monascaAPIStub.URL) + sut := &ClientImpl{ksClient: ksClientMock, monascaURL: monURL} + return ksClientMock, sut +} + +func TestValidRequest(t *testing.T) { + // setup + ksClientMock, sut := initClientSUT() + ksClientMock.On("GetToken").Return(testToken, nil).Once() + + // do + status, resp, err := sut.SendRequest("POST", "/metrics", expectedTransformed) + + // assert + assert.NoError(t, err) + assert.Equal(t, "", resp) + assert.Equal(t, status, http.StatusNoContent) + ksClientMock.AssertExpectations(t) +} + +func TestBadTokenRequest(t *testing.T) { + // setup + ksClientMock, sut := initClientSUT() + ksClientMock.On("GetToken").Return("blob", nil).Once() + + // do + status, resp, err := sut.SendRequest("POST", "/metrics", expectedTransformed) + + // assert + assert.NoError(t, err) + assert.Equal(t, monUnauthorizedResp, resp) + assert.Equal(t, http.StatusUnauthorized, status) + ksClientMock.AssertExpectations(t) +} + +func TestBadMetricsRequest(t *testing.T) { + // setup + ksClientMock, sut := initClientSUT() + ksClientMock.On("GetToken").Return(testToken, nil).Once() + + // do + status, _, err := sut.SendRequest("POST", "/metrics", "[1,2,3,4]") + + // assert + assert.NoError(t, err) + assert.Equal(t, status, http.StatusInternalServerError) + ksClientMock.AssertExpectations(t) +} + +func TestWrongURLRequest(t *testing.T) { + // setup + ksClientMock, sut := initClientSUT() + ksClientMock.On("GetToken").Return(testToken, nil).Once() + + // do + status, resp, err := sut.SendRequest("POST", "http:/malformed", expectedTransformed) + + // assert + assert.Error(t, err) + assert.Equal(t, "", resp) + assert.Equal(t, status, 0) + ksClientMock.AssertExpectations(t) +} + +func TestMonascaHealthy(t *testing.T) { + // setup + ksClientMock, sut := initClientSUT() + ksClientMock.On("GetToken").Return(testToken, nil).Once() + + // do + healthy := sut.CheckHealth() + + // assert + assert.True(t, healthy) + ksClientMock.AssertExpectations(t) +} + +func TestMonascaUnhealthy(t *testing.T) { + // TODO: reenable once #1232 is fixed + t.Skip("skipping test due to #1232") + // setup + ksClientMock := new(keystoneClientMock) + monURL, _ := url.Parse("http://unexisting.monasca.com") + sut := &ClientImpl{ksClient: ksClientMock, monascaURL: monURL} + ksClientMock.On("GetToken").Return(testToken, nil).Once() + + // do + healthy := sut.CheckHealth() + + // assert + assert.False(t, healthy) + ksClientMock.AssertExpectations(t) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver.go b/vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver.go new file mode 100644 index 0000000000..8d46a1f75e --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver.go @@ -0,0 +1,209 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package opentsdb + +import ( + "bytes" + "fmt" + "net/url" + "regexp" + "strings" + "sync" + "time" + + opentsdbclient "github.com/bluebreezecf/opentsdb-goclient/client" + opentsdbcfg "github.com/bluebreezecf/opentsdb-goclient/config" + "github.com/golang/glog" + "k8s.io/heapster/metrics/core" +) + +const ( + defaultTagName = "defaultTagName" + defaultTagValue = "defaultTagValue" + eventMetricName = "events" + eventUID = "uid" + opentsdbSinkName = "OpenTSDB Sink" + sinkRegisterName = "opentsdb" + defaultOpentsdbHost = "127.0.0.1:4242" + batchSize = 1000 +) + +var ( + // Matches any disallowed character in OpenTSDB names. + disallowedCharsRegexp = regexp.MustCompile("[^[:alnum:]\\-_\\./]") +) + +// openTSDBClient defines the minimal methods which will be used to +// communicate with the target OpenTSDB for current openTSDBSink instance. +type openTSDBClient interface { + Ping() error + Put(datapoints []opentsdbclient.DataPoint, queryParam string) (*opentsdbclient.PutResponse, error) +} + +type openTSDBSink struct { + client openTSDBClient + sync.RWMutex + writeFailures int + config opentsdbcfg.OpenTSDBConfig +} + +func (tsdbSink *openTSDBSink) ExportData(data *core.DataBatch) { + if err := tsdbSink.client.Ping(); err != nil { + glog.Warningf("Failed to ping opentsdb: %v", err) + return + } + dataPoints := make([]opentsdbclient.DataPoint, 0, batchSize) + for _, metricSet := range data.MetricSets { + for metricName, metricValue := range metricSet.MetricValues { + dataPoints = append(dataPoints, tsdbSink.metricToPoint(metricName, metricValue, data.Timestamp, metricSet.Labels)) + if len(dataPoints) >= batchSize { + _, err := tsdbSink.client.Put(dataPoints, opentsdbclient.PutRespWithSummary) + if err != nil { + glog.Errorf("failed to write metrics to opentsdb - %v", err) + tsdbSink.recordWriteFailure() + return + } + dataPoints = make([]opentsdbclient.DataPoint, 0, batchSize) + } + } + } + if len(dataPoints) >= 0 { + _, err := tsdbSink.client.Put(dataPoints, opentsdbclient.PutRespWithSummary) + if err != nil { + glog.Errorf("failed to write metrics to opentsdb - %v", err) + tsdbSink.recordWriteFailure() + return + } + } +} + +func (tsdbSink *openTSDBSink) DebugInfo() string { + buf := bytes.Buffer{} + buf.WriteString("Sink Type: OpenTSDB\n") + buf.WriteString(fmt.Sprintf("\tclient: Host %s", tsdbSink.config.OpentsdbHost)) + buf.WriteString(tsdbSink.getState()) + buf.WriteString("\n") + return buf.String() +} + +func (tsdbSink *openTSDBSink) Name() string { + return opentsdbSinkName +} + +func (tsdbSink *openTSDBSink) Stop() { + // Do nothing +} + +// Converts the given OpenTSDB metric or tag name/value to a form that is +// accepted by OpenTSDB. As the OpenTSDB documentation states: +// 'Metric names, tag names and tag values have to be made of alpha numeric +// characters, dash "-", underscore "_", period ".", and forward slash "/".' +func toValidOpenTsdbName(name string) (validName string) { + // This takes care of some cases where dash "-" characters were + // encoded as '\\x2d' in received Timeseries Points + validName = fmt.Sprintf("%s", name) + + // replace all illegal characters with '_' + return disallowedCharsRegexp.ReplaceAllLiteralString(validName, "_") +} + +// timeSeriesToPoint transfers the contents holding in the given pointer of sink_api.Timeseries +// into the instance of opentsdbclient.DataPoint +func (tsdbSink *openTSDBSink) metricToPoint(name string, value core.MetricValue, timestamp time.Time, labels map[string]string) opentsdbclient.DataPoint { + seriesName := strings.Replace(toValidOpenTsdbName(name), "/", "_", -1) + + if value.MetricType.String() != "" { + seriesName = fmt.Sprintf("%s_%s", seriesName, value.MetricType.String()) + } + + datapoint := opentsdbclient.DataPoint{ + Metric: seriesName, + Tags: make(map[string]string, len(labels)), + Timestamp: timestamp.Unix(), + } + if value.ValueType == core.ValueInt64 { + datapoint.Value = value.IntValue + } else { + datapoint.Value = value.FloatValue + } + + for key, value := range labels { + key = toValidOpenTsdbName(key) + value = toValidOpenTsdbName(value) + + if value != "" { + datapoint.Tags[key] = value + } + } + + tsdbSink.secureTags(&datapoint) + return datapoint +} + +// secureTags just fills in the default key-value pair for the tags, if there is no tags for +// current datapoint. Otherwise, the opentsdb will return error and the operation of putting +// datapoint will be failed. +func (tsdbSink *openTSDBSink) secureTags(datapoint *opentsdbclient.DataPoint) { + + glog.Warningf("%#v", datapoint) + if len(datapoint.Tags) == 0 { + datapoint.Tags[defaultTagName] = defaultTagValue + } +} + +func (tsdbSink *openTSDBSink) recordWriteFailure() { + tsdbSink.Lock() + defer tsdbSink.Unlock() + tsdbSink.writeFailures++ +} + +func (tsdbSink *openTSDBSink) getState() string { + tsdbSink.RLock() + defer tsdbSink.RUnlock() + return fmt.Sprintf("\tNumber of write failures: %d\n", tsdbSink.writeFailures) +} + +func (tsdbSink *openTSDBSink) ping() error { + return tsdbSink.client.Ping() +} + +func (tsdbSink *openTSDBSink) setupClient() error { + return nil +} + +func new(opentsdbHost string) (*openTSDBSink, error) { + cfg := opentsdbcfg.OpenTSDBConfig{OpentsdbHost: opentsdbHost} + opentsdbClient, err := opentsdbclient.NewClient(cfg) + if err != nil { + return nil, err + } + return &openTSDBSink{ + client: opentsdbClient, + config: cfg, + }, nil +} + +func CreateOpenTSDBSink(uri *url.URL) (core.DataSink, error) { + host := defaultOpentsdbHost + if len(uri.Host) > 0 { + host = uri.Host + } + tsdbSink, err := new(host) + if err != nil { + return nil, err + } + glog.Infof("created opentsdb sink with host: %v", host) + return tsdbSink, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver_test.go b/vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver_test.go new file mode 100644 index 0000000000..3fb0f8b192 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/opentsdb/driver_test.go @@ -0,0 +1,200 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package opentsdb + +import ( + "fmt" + "net/url" + "testing" + "time" + + opentsdb "github.com/bluebreezecf/opentsdb-goclient/client" + opentsdbcfg "github.com/bluebreezecf/opentsdb-goclient/config" + "github.com/stretchr/testify/assert" + "k8s.io/heapster/metrics/core" +) + +var ( + fakeOpenTSDBHost = "192.168.1.8:823" + fakeNodeIp = "192.168.1.23" + fakePodName = "redis-test" + fakePodUid = "redis-test-uid" + fakeLabel = map[string]string{ + "name": "redis", + "io.kubernetes.pod.name": "default/redis-test", + "pod_id": fakePodUid, + "pod_namespace": "default", + "pod_name": fakePodName, + "container_name": "redis", + "container_base_image": "kubernetes/redis:v1", + "namespace_id": "namespace-test-uid", + "host_id": fakeNodeIp, + } + errorPingFailed = fmt.Errorf("Failed to connect the target opentsdb.") + errorPutFailed = fmt.Errorf("The target opentsdb gets error and failed to store the datapoints.") +) + +type fakeOpenTSDBClient struct { + successfulPing bool + successfulPut bool + receivedDataPoints []opentsdb.DataPoint +} + +func (client *fakeOpenTSDBClient) Ping() error { + if client.successfulPing { + return nil + } + return errorPingFailed +} + +func (client *fakeOpenTSDBClient) Put(datapoints []opentsdb.DataPoint, queryParam string) (*opentsdb.PutResponse, error) { + if !client.successfulPut { + return nil, errorPutFailed + } + client.receivedDataPoints = append(client.receivedDataPoints, datapoints...) + putRes := opentsdb.PutResponse{ + StatusCode: 200, + Failed: 0, + Success: int64(len(datapoints)), + } + return &putRes, nil +} + +type fakeOpenTSDBSink struct { + *openTSDBSink + fakeClient *fakeOpenTSDBClient +} + +func NewFakeOpenTSDBSink(successfulPing, successfulPut bool) fakeOpenTSDBSink { + client := &fakeOpenTSDBClient{ + successfulPing: successfulPing, + successfulPut: successfulPut, + } + cfg := opentsdbcfg.OpenTSDBConfig{OpentsdbHost: fakeOpenTSDBHost} + return fakeOpenTSDBSink{ + &openTSDBSink{ + client: client, + config: cfg, + }, + client, + } +} + +func TestStoreTimeseriesEmptyInput(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(true, true) + db := core.DataBatch{} + fakeSink.ExportData(&db) + assert.Equal(t, 0, len(fakeSink.fakeClient.receivedDataPoints)) +} + +func TestStoreTimeseriesWithPingFailed(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(false, true) + batch := generateFakeBatch() + fakeSink.ExportData(batch) + assert.Equal(t, 0, len(fakeSink.fakeClient.receivedDataPoints)) +} + +func TestStoreTimeseriesWithPutFailed(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(true, false) + batch := generateFakeBatch() + fakeSink.ExportData(batch) + assert.Equal(t, 0, len(fakeSink.fakeClient.receivedDataPoints)) +} + +func TestStoreTimeseriesSingleTimeserieInput(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(true, true) + batch := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{}, + } + seriesName := "cpu/limit" + batch.MetricSets["m1"] = generateMetricSet(seriesName, core.MetricGauge, 1000) + batch.MetricSets["m1"].Labels = map[string]string{} + fakeSink.ExportData(&batch) + assert.Equal(t, 1, len(fakeSink.fakeClient.receivedDataPoints)) + assert.Equal(t, "cpu_limit_gauge", fakeSink.fakeClient.receivedDataPoints[0].Metric) + //tsdbSink.secureTags() add a default tag key and value pair + assert.Equal(t, 1, len(fakeSink.fakeClient.receivedDataPoints[0].Tags)) + assert.Equal(t, defaultTagValue, fakeSink.fakeClient.receivedDataPoints[0].Tags[defaultTagName]) +} + +func TestStoreTimeseriesMultipleTimeseriesInput(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(true, true) + batch := generateFakeBatch() + fakeSink.ExportData(batch) + assert.Equal(t, len(batch.MetricSets), len(fakeSink.fakeClient.receivedDataPoints)) +} +func TestName(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(true, true) + name := fakeSink.Name() + assert.Equal(t, name, opentsdbSinkName) +} + +func TestDebugInfo(t *testing.T) { + fakeSink := NewFakeOpenTSDBSink(true, true) + debugInfo := fakeSink.DebugInfo() + assert.Contains(t, debugInfo, "Sink Type: OpenTSDB") + assert.Contains(t, debugInfo, "client: Host "+fakeOpenTSDBHost) + assert.Contains(t, debugInfo, "Number of write failures:") +} + +func TestCreateOpenTSDBSinkWithEmptyInputs(t *testing.T) { + sink, err := CreateOpenTSDBSink(&url.URL{}) + assert.NoError(t, err) + assert.NotNil(t, sink) + tsdbSink, ok := sink.(*openTSDBSink) + assert.Equal(t, true, ok) + assert.Equal(t, defaultOpentsdbHost, tsdbSink.config.OpentsdbHost) +} + +func TestCreateOpenTSDBSinkWithNoEmptyInputs(t *testing.T) { + fakeOpentsdbHost := "192.168.8.23:4242" + sink, err := CreateOpenTSDBSink(&url.URL{Host: fakeOpentsdbHost}) + assert.NoError(t, err) + assert.NotNil(t, sink) + tsdbSink, ok := sink.(*openTSDBSink) + assert.Equal(t, true, ok) + assert.Equal(t, fakeOpentsdbHost, tsdbSink.config.OpentsdbHost) +} + +func generateFakeBatch() *core.DataBatch { + batch := core.DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*core.MetricSet{}, + } + + batch.MetricSets["m1"] = generateMetricSet("cpu/limit", core.MetricGauge, 1000) + batch.MetricSets["m2"] = generateMetricSet("cpu/usage", core.MetricCumulative, 43363664) + batch.MetricSets["m3"] = generateMetricSet("filesystem/limit", core.MetricGauge, 42241163264) + batch.MetricSets["m4"] = generateMetricSet("filesystem/usage", core.MetricGauge, 32768) + batch.MetricSets["m5"] = generateMetricSet("memory/limit", core.MetricGauge, -1) + batch.MetricSets["m6"] = generateMetricSet("memory/usage", core.MetricGauge, 487424) + batch.MetricSets["m7"] = generateMetricSet("memory/working_set", core.MetricGauge, 491520) + batch.MetricSets["m8"] = generateMetricSet("uptime", core.MetricCumulative, 910823) + return &batch +} + +func generateMetricSet(name string, metricType core.MetricType, value int64) *core.MetricSet { + return &core.MetricSet{ + Labels: fakeLabel, + MetricValues: map[string]core.MetricValue{ + name: core.MetricValue{ + MetricType: metricType, + ValueType: core.ValueInt64, + IntValue: value, + }, + }, + } +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/riemann/driver.go b/vendor/k8s.io/heapster/metrics/sinks/riemann/driver.go new file mode 100644 index 0000000000..75d0eec2de --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/riemann/driver.go @@ -0,0 +1,213 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package riemann + +import ( + "net/url" + "reflect" + "runtime" + "strconv" + "sync" + + "time" + + riemann_api "github.com/bigdatadev/goryman" + "github.com/golang/glog" + "k8s.io/heapster/metrics/core" +) + +// Abstracted for testing: this package works against any client that obeys the +// interface contract exposed by the goryman Riemann client + +type riemannClient interface { + Connect() error + Close() error + SendEvent(e *riemann_api.Event) error +} + +type riemannSink struct { + client riemannClient + config riemannConfig + sync.RWMutex +} + +type riemannConfig struct { + host string + ttl float32 + state string + tags []string +} + +const ( + // Maximum number of riemann Events to be sent in one batch. + maxSendBatchSize = 10000 + max_retries = 2 +) + +func CreateRiemannSink(uri *url.URL) (core.DataSink, error) { + c := riemannConfig{ + host: "riemann-heapster:5555", + ttl: 60.0, + state: "", + tags: make([]string, 0), + } + if len(uri.Host) > 0 { + c.host = uri.Host + } + options := uri.Query() + if len(options["ttl"]) > 0 { + var ttl, err = strconv.ParseFloat(options["ttl"][0], 32) + if err != nil { + return nil, err + } + c.ttl = float32(ttl) + } + if len(options["state"]) > 0 { + c.state = options["state"][0] + } + if len(options["tags"]) > 0 { + c.tags = options["tags"] + } + + glog.Infof("Riemann sink URI: '%+v', host: '%+v', options: '%+v', ", uri, c.host, options) + rs := &riemannSink{ + client: nil, + config: c, + } + + err := rs.setupRiemannClient() + if err != nil { + glog.Warningf("Riemann sink not connected: %v", err) + // Warn but return the sink. + } + return rs, nil +} + +func (rs *riemannSink) setupRiemannClient() error { + client := riemann_api.NewGorymanClient(rs.config.host) + runtime.SetFinalizer(client, func(c riemannClient) { c.Close() }) + err := client.Connect() + if err != nil { + return err + } + rs.client = client + return nil +} + +// Return a user-friendly string describing the sink +func (sink *riemannSink) Name() string { + return "Riemann Sink" +} + +func (sink *riemannSink) Stop() { + // nothing needs to be done. +} + +// ExportData Send a collection of Timeseries to Riemann +func (sink *riemannSink) ExportData(dataBatch *core.DataBatch) { + sink.Lock() + defer sink.Unlock() + + if sink.client == nil { + if err := sink.setupRiemannClient(); err != nil { + glog.Warningf("Riemann sink not connected: %v", err) + return + } + } + + var dataEvents []riemann_api.Event + appendMetric := func(host, name string, value interface{}, labels map[string]string) { + event := riemann_api.Event{ + Time: dataBatch.Timestamp.Unix(), + Service: name, + Host: host, + Description: "", //no description - waste of bandwidth. + Attributes: labels, + Metric: value, + Ttl: sink.config.ttl, + State: sink.config.state, + Tags: sink.config.tags, + } + + dataEvents = append(dataEvents, event) + if len(dataEvents) >= maxSendBatchSize { + sink.sendData(dataEvents) + dataEvents = nil + } + } + + riemannValue := func(value interface{}) interface{} { + // Workaround for error from goryman: "Metric of invalid type (type int64)" + if reflect.TypeOf(value).Kind() == reflect.Int64 { + return int(value.(int64)) + } + return value + } + + for _, metricSet := range dataBatch.MetricSets { + host := metricSet.Labels[core.LabelHostname.Key] + for metricName, metricValue := range metricSet.MetricValues { + if value := metricValue.GetValue(); value != nil { + appendMetric(host, metricName, riemannValue(value), metricSet.Labels) + } + } + for _, metric := range metricSet.LabeledMetrics { + if value := metric.GetValue(); value != nil { + labels := make(map[string]string) + for k, v := range metricSet.Labels { + labels[k] = v + } + for k, v := range metric.Labels { + labels[k] = v + } + appendMetric(host, metric.Name, riemannValue(value), labels) + } + } + } + + if len(dataEvents) > 0 { + sink.sendData(dataEvents) + } +} + +func (sink *riemannSink) sendData(dataEvents []riemann_api.Event) { + if sink.client == nil { + return + } + + start := time.Now() + errors := 0 + for _, event := range dataEvents { + glog.V(8).Infof("Sending event to Riemann: %+v", event) + var err error + for try := 0; try < max_retries; try++ { + err = sink.client.SendEvent(&event) + if err == nil { + break + } + } + if err != nil { + errors++ + glog.V(4).Infof("Failed to send event to Riemann: %+v: %+v", event, err) + } + } + end := time.Now() + if errors > 0 { + glog.V(2).Info("There were errors sending events to Riemman, forcing reconnection") + sink.client.Close() + sink.client = nil + } + glog.V(4).Infof("Exported %d events to riemann in %s", len(dataEvents)-errors, end.Sub(start)) +} diff --git a/vendor/k8s.io/heapster/metrics/sinks/riemann/driver_test.go b/vendor/k8s.io/heapster/metrics/sinks/riemann/driver_test.go new file mode 100644 index 0000000000..1054a08413 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sinks/riemann/driver_test.go @@ -0,0 +1,197 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package riemann + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + riemann_api "github.com/bigdatadev/goryman" + "github.com/stretchr/testify/assert" + "k8s.io/heapster/metrics/core" +) + +type eventSendToRiemann struct { + event string +} + +type fakeRiemannClient struct { + events []eventSendToRiemann +} + +type fakeRiemannSink struct { + core.DataSink + fakeRiemannClient *fakeRiemannClient +} + +func NewFakeRiemannClient() *fakeRiemannClient { + return &fakeRiemannClient{[]eventSendToRiemann{}} +} + +func (client *fakeRiemannClient) Connect() error { + return nil +} + +func (client *fakeRiemannClient) Close() error { + return nil +} + +func (client *fakeRiemannClient) SendEvent(e *riemann_api.Event) error { + eventsJson, _ := json.Marshal(e) + client.events = append(client.events, eventSendToRiemann{event: string(eventsJson)}) + return nil +} + +// Returns a fake kafka sink. +func NewFakeSink() fakeRiemannSink { + riemannClient := NewFakeRiemannClient() + c := riemannConfig{ + host: "riemann-heapster:5555", + ttl: 60.0, + state: "", + tags: make([]string, 0), + } + + return fakeRiemannSink{ + &riemannSink{ + client: riemannClient, + config: c, + }, + riemannClient, + } +} + +func TestStoreDataEmptyInput(t *testing.T) { + fakeSink := NewFakeSink() + dataBatch := core.DataBatch{} + fakeSink.ExportData(&dataBatch) + assert.Equal(t, 0, len(fakeSink.fakeRiemannClient.events)) +} + +func TestStoreMultipleDataInput(t *testing.T) { + fakeSink := NewFakeSink() + timestamp := time.Now() + + l := make(map[string]string) + l["namespace_id"] = "123" + l["container_name"] = "/system.slice/-.mount" + l[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l2 := make(map[string]string) + l2["namespace_id"] = "123" + l2["container_name"] = "/system.slice/dbus.service" + l2[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l3 := make(map[string]string) + l3["namespace_id"] = "123" + l3[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l4 := make(map[string]string) + l4["namespace_id"] = "" + l4[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + l5 := make(map[string]string) + l5["namespace_id"] = "123" + l5[core.LabelPodId.Key] = "aaaa-bbbb-cccc-dddd" + + metricSet1 := core.MetricSet{ + Labels: l, + MetricValues: map[string]core.MetricValue{ + "/system.slice/-.mount//cpu/limit": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet2 := core.MetricSet{ + Labels: l2, + MetricValues: map[string]core.MetricValue{ + "/system.slice/dbus.service//cpu/usage": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet3 := core.MetricSet{ + Labels: l3, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet4 := core.MetricSet{ + Labels: l4, + MetricValues: map[string]core.MetricValue{ + "test/metric/1": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + metricSet5 := core.MetricSet{ + Labels: l5, + MetricValues: map[string]core.MetricValue{ + "removeme": { + ValueType: core.ValueInt64, + MetricType: core.MetricCumulative, + IntValue: 123456, + }, + }, + } + + data := core.DataBatch{ + Timestamp: timestamp, + MetricSets: map[string]*core.MetricSet{ + "pod1": &metricSet1, + "pod2": &metricSet2, + "pod3": &metricSet3, + "pod4": &metricSet4, + "pod5": &metricSet5, + }, + } + + timeValue := timestamp.Unix() + + fakeSink.ExportData(&data) + + //expect msg string + assert.Equal(t, 5, len(fakeSink.fakeRiemannClient.events)) + + var expectEventsTemplate = [5]string{ + `{"Ttl":60,"Time":%d,"Tags":[],"Host":"","State":"","Service":"/system.slice/-.mount//cpu/limit","Metric":123456,"Description":"","Attributes":{"container_name":"/system.slice/-.mount","namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"Ttl":60,"Time":%d,"Tags":[],"Host":"","State":"","Service":"/system.slice/dbus.service//cpu/usage","Metric":123456,"Description":"","Attributes":{"container_name":"/system.slice/dbus.service","namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"Ttl":60,"Time":%d,"Tags":[],"Host":"","State":"","Service":"test/metric/1","Metric":123456,"Description":"","Attributes":{"namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"Ttl":60,"Time":%d,"Tags":[],"Host":"","State":"","Service":"test/metric/1","Metric":123456,"Description":"","Attributes":{"namespace_id":"","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + `{"Ttl":60,"Time":%d,"Tags":[],"Host":"","State":"","Service":"removeme","Metric":123456,"Description":"","Attributes":{"namespace_id":"123","pod_id":"aaaa-bbbb-cccc-dddd"}}`, + } + + eventsString := fmt.Sprintf("%s", fakeSink.fakeRiemannClient.events) + for _, evtTemplate := range expectEventsTemplate { + expectEvt := fmt.Sprintf(evtTemplate, timeValue) + assert.Contains(t, eventsString, expectEvt) + } +} diff --git a/vendor/k8s.io/heapster/metrics/sources/factory.go b/vendor/k8s.io/heapster/metrics/sources/factory.go new file mode 100644 index 0000000000..b422d49b4b --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/factory.go @@ -0,0 +1,51 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sources + +import ( + "fmt" + + "k8s.io/heapster/common/flags" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/sources/kubelet" + "k8s.io/heapster/metrics/sources/summary" +) + +type SourceFactory struct { +} + +func (this *SourceFactory) Build(uri flags.Uri) (core.MetricsSourceProvider, error) { + switch uri.Key { + case "kubernetes": + provider, err := kubelet.NewKubeletProvider(&uri.Val) + return provider, err + case "kubernetes.summary_api": + provider, err := summary.NewSummaryProvider(&uri.Val) + return provider, err + default: + return nil, fmt.Errorf("Source not recognized: %s", uri.Key) + } +} + +func (this *SourceFactory) BuildAll(uris flags.Uris) (core.MetricsSourceProvider, error) { + if len(uris) != 1 { + return nil, fmt.Errorf("Only one source is supported") + } + return this.Build(uris[0]) +} + +func NewSourceFactory() *SourceFactory { + return &SourceFactory{} +} diff --git a/vendor/k8s.io/heapster/metrics/sources/kubelet/cadvisor_utils.go b/vendor/k8s.io/heapster/metrics/sources/kubelet/cadvisor_utils.go new file mode 100644 index 0000000000..8b8a3d0b58 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/kubelet/cadvisor_utils.go @@ -0,0 +1,23 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubelet + +import ( + cadvisor "github.com/google/cadvisor/info/v1" +) + +func isNode(c *cadvisor.ContainerInfo) bool { + return c.Name == "/" +} diff --git a/vendor/k8s.io/heapster/metrics/sources/kubelet/configs.go b/vendor/k8s.io/heapster/metrics/sources/kubelet/configs.go new file mode 100644 index 0000000000..9a93c65d8e --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/kubelet/configs.go @@ -0,0 +1,71 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubelet + +import ( + "net/url" + "strconv" + + "github.com/golang/glog" + kube_config "k8s.io/heapster/common/kubernetes" + kube_client "k8s.io/kubernetes/pkg/client/restclient" + kubelet_client "k8s.io/kubernetes/pkg/kubelet/client" +) + +const ( + APIVersion = "v1" + + defaultKubeletPort = 10255 + defaultKubeletHttps = false + defaultUseServiceAccount = false + defaultServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" + defaultInClusterConfig = true +) + +func GetKubeConfigs(uri *url.URL) (*kube_client.Config, *kubelet_client.KubeletClientConfig, error) { + + kubeConfig, err := kube_config.GetKubeClientConfig(uri) + if err != nil { + return nil, nil, err + } + opts := uri.Query() + + kubeletPort := defaultKubeletPort + if len(opts["kubeletPort"]) >= 1 { + kubeletPort, err = strconv.Atoi(opts["kubeletPort"][0]) + if err != nil { + return nil, nil, err + } + } + + kubeletHttps := defaultKubeletHttps + if len(opts["kubeletHttps"]) >= 1 { + kubeletHttps, err = strconv.ParseBool(opts["kubeletHttps"][0]) + if err != nil { + return nil, nil, err + } + } + glog.Infof("Using Kubernetes client with master %q and version %+v\n", kubeConfig.Host, kubeConfig.GroupVersion) + glog.Infof("Using kubelet port %d", kubeletPort) + + kubeletConfig := &kubelet_client.KubeletClientConfig{ + Port: uint(kubeletPort), + EnableHttps: kubeletHttps, + TLSClientConfig: kubeConfig.TLSClientConfig, + BearerToken: kubeConfig.BearerToken, + } + + return kubeConfig, kubeletConfig, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet.go b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet.go new file mode 100644 index 0000000000..f2d78d0f62 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet.go @@ -0,0 +1,347 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubelet + +import ( + "fmt" + "net/url" + "strings" + "time" + + . "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" + cadvisor "github.com/google/cadvisor/info/v1" + "github.com/prometheus/client_golang/prometheus" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + kube_client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/labels" +) + +const ( + infraContainerName = "POD" + // TODO: following constants are copied from k8s, change to use them directly + kubernetesPodNameLabel = "io.kubernetes.pod.name" + kubernetesPodNamespaceLabel = "io.kubernetes.pod.namespace" + kubernetesPodUID = "io.kubernetes.pod.uid" + kubernetesContainerLabel = "io.kubernetes.container.name" +) + +var ( + // The Kubelet request latencies in microseconds. + kubeletRequestLatency = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "heapster", + Subsystem: "kubelet", + Name: "request_duration_microseconds", + Help: "The Kubelet request latencies in microseconds.", + }, + []string{"node"}, + ) +) + +func init() { + prometheus.MustRegister(kubeletRequestLatency) +} + +// Kubelet-provided metrics for pod and system container. +type kubeletMetricsSource struct { + host Host + kubeletClient *KubeletClient + nodename string + hostname string + hostId string +} + +func NewKubeletMetricsSource(host Host, client *KubeletClient, nodeName string, hostName string, hostId string) MetricsSource { + return &kubeletMetricsSource{ + host: host, + kubeletClient: client, + nodename: nodeName, + hostname: hostName, + hostId: hostId, + } +} + +func (this *kubeletMetricsSource) Name() string { + return this.String() +} + +func (this *kubeletMetricsSource) String() string { + return fmt.Sprintf("kubelet:%s:%d", this.host.IP, this.host.Port) +} + +func (this *kubeletMetricsSource) handleSystemContainer(c *cadvisor.ContainerInfo, cMetrics *MetricSet) string { + glog.V(8).Infof("Found system container %v with labels: %+v", c.Name, c.Spec.Labels) + cName := c.Name + if strings.HasPrefix(cName, "/") { + cName = cName[1:] + } + cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer + cMetrics.Labels[LabelContainerName.Key] = cName + return NodeContainerKey(this.nodename, cName) +} + +func (this *kubeletMetricsSource) handleKubernetesContainer(cName, ns, podName string, c *cadvisor.ContainerInfo, cMetrics *MetricSet) string { + var metricSetKey string + if cName == infraContainerName { + metricSetKey = PodKey(ns, podName) + cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePod + } else { + metricSetKey = PodContainerKey(ns, podName, cName) + cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer + cMetrics.Labels[LabelContainerName.Key] = cName + cMetrics.Labels[LabelContainerBaseImage.Key] = c.Spec.Image + } + cMetrics.Labels[LabelPodId.Key] = c.Spec.Labels[kubernetesPodUID] + cMetrics.Labels[LabelPodName.Key] = podName + cMetrics.Labels[LabelNamespaceName.Key] = ns + // Needed for backward compatibility + cMetrics.Labels[LabelPodNamespace.Key] = ns + return metricSetKey +} + +func (this *kubeletMetricsSource) decodeMetrics(c *cadvisor.ContainerInfo) (string, *MetricSet) { + if len(c.Stats) == 0 { + return "", nil + } + + var metricSetKey string + cMetrics := &MetricSet{ + CreateTime: c.Spec.CreationTime, + ScrapeTime: c.Stats[0].Timestamp, + MetricValues: map[string]MetricValue{}, + Labels: map[string]string{ + LabelNodename.Key: this.nodename, + LabelHostname.Key: this.hostname, + LabelHostID.Key: this.hostId, + }, + LabeledMetrics: []LabeledMetric{}, + } + + if isNode(c) { + metricSetKey = NodeKey(this.nodename) + cMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode + } else { + cName := c.Spec.Labels[kubernetesContainerLabel] + ns := c.Spec.Labels[kubernetesPodNamespaceLabel] + podName := c.Spec.Labels[kubernetesPodNameLabel] + + // Support for kubernetes 1.0.* + if ns == "" && strings.Contains(podName, "/") { + tokens := strings.SplitN(podName, "/", 2) + if len(tokens) == 2 { + ns = tokens[0] + podName = tokens[1] + } + } + if cName == "" { + // Better this than nothing. This is a temporary hack for new heapster to work + // with Kubernetes 1.0.*. + // TODO: fix this with POD list. + // Parsing name like: + // k8s_kube-ui.7f9b83f6_kube-ui-v1-bxj1w_kube-system_9abfb0bd-811f-11e5-b548-42010af00002_e6841e8d + pos := strings.Index(c.Name, ".") + if pos >= 0 { + // remove first 4 chars. + cName = c.Name[len("k8s_"):pos] + } + } + + // No Kubernetes metadata so treat this as a system container. + if cName == "" || ns == "" || podName == "" { + metricSetKey = this.handleSystemContainer(c, cMetrics) + } else { + metricSetKey = this.handleKubernetesContainer(cName, ns, podName, c, cMetrics) + } + } + + for _, metric := range StandardMetrics { + if metric.HasValue != nil && metric.HasValue(&c.Spec) { + cMetrics.MetricValues[metric.Name] = metric.GetValue(&c.Spec, c.Stats[0]) + } + } + + for _, metric := range LabeledMetrics { + if metric.HasLabeledMetric != nil && metric.HasLabeledMetric(&c.Spec) { + labeledMetrics := metric.GetLabeledMetric(&c.Spec, c.Stats[0]) + cMetrics.LabeledMetrics = append(cMetrics.LabeledMetrics, labeledMetrics...) + } + } + + if c.Spec.HasCustomMetrics { + metricloop: + for _, spec := range c.Spec.CustomMetrics { + if cmValue, ok := c.Stats[0].CustomMetrics[spec.Name]; ok && cmValue != nil && len(cmValue) >= 1 { + newest := cmValue[0] + for _, metricVal := range cmValue { + if newest.Timestamp.Before(metricVal.Timestamp) { + newest = metricVal + } + } + mv := MetricValue{} + switch spec.Type { + case cadvisor.MetricGauge: + mv.MetricType = MetricGauge + case cadvisor.MetricCumulative: + mv.MetricType = MetricCumulative + default: + glog.V(4).Infof("Skipping %s: unknown custom metric type: %v", spec.Name, spec.Type) + continue metricloop + } + + switch spec.Format { + case cadvisor.IntType: + mv.ValueType = ValueInt64 + mv.IntValue = newest.IntValue + case cadvisor.FloatType: + mv.ValueType = ValueFloat + mv.FloatValue = float32(newest.FloatValue) + default: + glog.V(4).Infof("Skipping %s: unknown custom metric format", spec.Name, spec.Format) + continue metricloop + } + + cMetrics.MetricValues[CustomMetricPrefix+spec.Name] = mv + } + } + } + + return metricSetKey, cMetrics +} + +func (this *kubeletMetricsSource) ScrapeMetrics(start, end time.Time) *DataBatch { + containers, err := this.scrapeKubelet(this.kubeletClient, this.host, start, end) + if err != nil { + glog.Errorf("error while getting containers from Kubelet: %v", err) + } + glog.V(2).Infof("successfully obtained stats for %v containers", len(containers)) + + result := &DataBatch{ + Timestamp: end, + MetricSets: map[string]*MetricSet{}, + } + keys := make(map[string]bool) + for _, c := range containers { + name, metrics := this.decodeMetrics(&c) + if name == "" || metrics == nil { + continue + } + result.MetricSets[name] = metrics + keys[name] = true + } + return result +} + +func (this *kubeletMetricsSource) scrapeKubelet(client *KubeletClient, host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) { + startTime := time.Now() + defer kubeletRequestLatency.WithLabelValues(this.hostname).Observe(float64(time.Since(startTime))) + return client.GetAllRawContainers(host, start, end) +} + +type kubeletProvider struct { + nodeLister *cache.StoreToNodeLister + reflector *cache.Reflector + kubeletClient *KubeletClient +} + +func (this *kubeletProvider) GetMetricsSources() []MetricsSource { + sources := []MetricsSource{} + nodes, err := this.nodeLister.List() + if err != nil { + glog.Errorf("error while listing nodes: %v", err) + return sources + } + if len(nodes.Items) == 0 { + glog.Error("No nodes received from APIserver.") + return sources + } + + nodeNames := make(map[string]bool) + for _, node := range nodes.Items { + nodeNames[node.Name] = true + hostname, ip, err := getNodeHostnameAndIP(&node) + if err != nil { + glog.Errorf("%v", err) + continue + } + sources = append(sources, NewKubeletMetricsSource( + Host{IP: ip, Port: this.kubeletClient.GetPort()}, + this.kubeletClient, + node.Name, + hostname, + node.Spec.ExternalID, + )) + } + return sources +} + +func getNodeHostnameAndIP(node *kube_api.Node) (string, string, error) { + for _, c := range node.Status.Conditions { + if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue { + return "", "", fmt.Errorf("Node %v is not ready", node.Name) + } + } + hostname, ip := node.Name, "" + for _, addr := range node.Status.Addresses { + if addr.Type == kube_api.NodeHostName && addr.Address != "" { + hostname = addr.Address + } + if addr.Type == kube_api.NodeInternalIP && addr.Address != "" { + ip = addr.Address + } + if addr.Type == kube_api.NodeLegacyHostIP && addr.Address != "" && ip == "" { + ip = addr.Address + } + } + if ip != "" { + return hostname, ip, nil + } + return "", "", fmt.Errorf("Node %v has no valid hostname and/or IP address: %v %v", node.Name, hostname, ip) +} + +func NewKubeletProvider(uri *url.URL) (MetricsSourceProvider, error) { + // create clients + kubeConfig, kubeletConfig, err := GetKubeConfigs(uri) + if err != nil { + return nil, err + } + kubeClient := kube_client.NewOrDie(kubeConfig) + kubeletClient, err := NewKubeletClient(kubeletConfig) + if err != nil { + return nil, err + } + + // Get nodes to test if the client is configured well. Watch gives less error information. + if _, err := kubeClient.Nodes().List(kube_api.ListOptions{ + LabelSelector: labels.Everything(), + FieldSelector: fields.Everything()}); err != nil { + glog.Errorf("Failed to load nodes: %v", err) + } + + // watch nodes + lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything()) + nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)} + reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, time.Hour) + reflector.Run() + + return &kubeletProvider{ + nodeLister: nodeLister, + reflector: reflector, + kubeletClient: kubeletClient, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client.go b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client.go new file mode 100644 index 0000000000..2bb446870c --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client.go @@ -0,0 +1,210 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements a cadvisor datasource, that collects metrics from an instance +// of cadvisor runing on a specific host. + +package kubelet + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "time" + + cadvisor "github.com/google/cadvisor/info/v1" + "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats" + kube_client "k8s.io/kubernetes/pkg/kubelet/client" +) + +type Host struct { + IP string + Port int + Resource string +} + +type KubeletClient struct { + config *kube_client.KubeletClientConfig + client *http.Client +} + +type ErrNotFound struct { + endpoint string +} + +func (err *ErrNotFound) Error() string { + return fmt.Sprintf("%q not found", err.endpoint) +} + +func IsNotFoundError(err error) bool { + _, isNotFound := err.(*ErrNotFound) + return isNotFound +} + +func sampleContainerStats(stats []*cadvisor.ContainerStats) []*cadvisor.ContainerStats { + if len(stats) == 0 { + return []*cadvisor.ContainerStats{} + } + return []*cadvisor.ContainerStats{stats[len(stats)-1]} +} + +func (self *KubeletClient) postRequestAndGetValue(client *http.Client, req *http.Request, value interface{}) error { + response, err := client.Do(req) + if err != nil { + return err + } + defer response.Body.Close() + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return fmt.Errorf("failed to read response body - %v", err) + } + if response.StatusCode == http.StatusNotFound { + return &ErrNotFound{req.URL.String()} + } else if response.StatusCode != http.StatusOK { + return fmt.Errorf("request failed - %q, response: %q", response.Status, string(body)) + } + err = json.Unmarshal(body, value) + if err != nil { + return fmt.Errorf("failed to parse output. Response: %q. Error: %v", string(body), err) + } + return nil +} + +func (self *KubeletClient) parseStat(containerInfo *cadvisor.ContainerInfo) *cadvisor.ContainerInfo { + containerInfo.Stats = sampleContainerStats(containerInfo.Stats) + if len(containerInfo.Aliases) > 0 { + containerInfo.Name = containerInfo.Aliases[0] + } + return containerInfo +} + +// TODO(vmarmol): Use Kubernetes' if we export it as an API. +type statsRequest struct { + // The name of the container for which to request stats. + // Default: / + ContainerName string `json:"containerName,omitempty"` + + // Max number of stats to return. + // If start and end time are specified this limit is ignored. + // Default: 60 + NumStats int `json:"num_stats,omitempty"` + + // Start time for which to query information. + // If ommitted, the beginning of time is assumed. + Start time.Time `json:"start,omitempty"` + + // End time for which to query information. + // If ommitted, current time is assumed. + End time.Time `json:"end,omitempty"` + + // Whether to also include information from subcontainers. + // Default: false. + Subcontainers bool `json:"subcontainers,omitempty"` +} + +// Get stats for all non-Kubernetes containers. +func (self *KubeletClient) GetAllRawContainers(host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) { + scheme := "http" + if self.config != nil && self.config.EnableHttps { + scheme = "https" + } + + url := fmt.Sprintf("%s://%s:%d/stats/container/", scheme, host.IP, host.Port) + + return self.getAllContainers(url, start, end) +} + +func (self *KubeletClient) GetSummary(host Host) (*stats.Summary, error) { + url := url.URL{ + Scheme: "http", + Host: fmt.Sprintf("%s:%d", host.IP, host.Port), + Path: "/stats/summary/", + } + if self.config != nil && self.config.EnableHttps { + url.Scheme = "https" + } + + req, err := http.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, err + } + summary := &stats.Summary{} + client := self.client + if client == nil { + client = http.DefaultClient + } + err = self.postRequestAndGetValue(client, req, summary) + return summary, err +} + +func (self *KubeletClient) GetPort() int { + return int(self.config.Port) +} + +func (self *KubeletClient) getAllContainers(url string, start, end time.Time) ([]cadvisor.ContainerInfo, error) { + // Request data from all subcontainers. + request := statsRequest{ + ContainerName: "/", + NumStats: 1, + Start: start, + End: end, + Subcontainers: true, + } + body, err := json.Marshal(request) + if err != nil { + return nil, err + } + req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + + var containers map[string]cadvisor.ContainerInfo + client := self.client + if client == nil { + client = http.DefaultClient + } + err = self.postRequestAndGetValue(client, req, &containers) + if err != nil { + return nil, fmt.Errorf("failed to get all container stats from Kubelet URL %q: %v", url, err) + } + + result := make([]cadvisor.ContainerInfo, 0, len(containers)) + for _, containerInfo := range containers { + cont := self.parseStat(&containerInfo) + if cont != nil { + result = append(result, *cont) + } + } + return result, nil +} + +func NewKubeletClient(kubeletConfig *kube_client.KubeletClientConfig) (*KubeletClient, error) { + transport, err := kube_client.MakeTransport(kubeletConfig) + if err != nil { + return nil, err + } + c := &http.Client{ + Transport: transport, + Timeout: kubeletConfig.HTTPTimeout, + } + return &KubeletClient{ + config: kubeletConfig, + client: c, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client_test.go b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client_test.go new file mode 100644 index 0000000000..ff0b85b217 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_client_test.go @@ -0,0 +1,104 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubelet + +import ( + "encoding/json" + "net/http/httptest" + "testing" + "time" + + cadvisor_api "github.com/google/cadvisor/info/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + util "k8s.io/kubernetes/pkg/util/testing" +) + +func checkContainer(t *testing.T, expected cadvisor_api.ContainerInfo, actual cadvisor_api.ContainerInfo) { + assert.True(t, actual.Spec.Eq(&expected.Spec)) + for i, stat := range actual.Stats { + assert.True(t, stat.Eq(expected.Stats[i])) + } +} + +func TestAllContainers(t *testing.T) { + rootContainer := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + HasMemory: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + }, + }, + } + + subcontainer := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/sub", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + HasMemory: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + }, + }, + } + response := map[string]cadvisor_api.ContainerInfo{ + rootContainer.Name: { + ContainerReference: cadvisor_api.ContainerReference{ + Name: rootContainer.Name, + }, + Spec: rootContainer.Spec, + Stats: []*cadvisor_api.ContainerStats{ + rootContainer.Stats[0], + }, + }, + subcontainer.Name: { + ContainerReference: cadvisor_api.ContainerReference{ + Name: subcontainer.Name, + }, + Spec: subcontainer.Spec, + Stats: []*cadvisor_api.ContainerStats{ + subcontainer.Stats[0], + }, + }, + } + data, err := json.Marshal(&response) + require.NoError(t, err) + handler := util.FakeHandler{ + StatusCode: 200, + RequestBody: "", + ResponseBody: string(data), + T: t, + } + server := httptest.NewServer(&handler) + defer server.Close() + kubeletClient := KubeletClient{} + containers, err := kubeletClient.getAllContainers(server.URL, time.Now(), time.Now().Add(time.Minute)) + require.NoError(t, err) + require.Len(t, containers, 2) + checkContainer(t, rootContainer, containers[0]) + checkContainer(t, subcontainer, containers[1]) +} diff --git a/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_test.go b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_test.go new file mode 100644 index 0000000000..4b22b33a9b --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/kubelet/kubelet_test.go @@ -0,0 +1,463 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package kubelet + +import ( + "encoding/json" + "net/http/httptest" + "strconv" + "strings" + "testing" + "time" + + cadvisor_api "github.com/google/cadvisor/info/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/heapster/metrics/core" + kube_api "k8s.io/kubernetes/pkg/api" + util "k8s.io/kubernetes/pkg/util/testing" +) + +func TestDecodeMetrics1(t *testing.T) { + kMS := kubeletMetricsSource{ + nodename: "test", + hostname: "test-hostname", + } + c1 := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + Cpu: cadvisor_api.CpuStats{ + Usage: cadvisor_api.CpuUsage{ + Total: 100, + PerCpu: []uint64{5, 10}, + User: 1, + System: 1, + }, + LoadAverage: 20, + }, + }, + }, + } + metricSetKey, metricSet := kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "node:test") + assert.Equal(t, metricSet.Labels[core.LabelMetricSetType.Key], core.MetricSetTypeNode) +} + +func TestDecodeMetrics2(t *testing.T) { + kMS := kubeletMetricsSource{ + nodename: "test", + hostname: "test-hostname", + } + c1 := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + Cpu: cadvisor_api.CpuStats{ + Usage: cadvisor_api.CpuUsage{ + Total: 100, + PerCpu: []uint64{5, 10}, + User: 1, + System: 1, + }, + LoadAverage: 20, + }, + }, + }, + } + metricSetKey, metricSet := kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "node:test") + assert.Equal(t, metricSet.Labels[core.LabelMetricSetType.Key], core.MetricSetTypeNode) +} + +func TestDecodeMetrics3(t *testing.T) { + kMS := kubeletMetricsSource{ + nodename: "test", + hostname: "test-hostname", + } + c1 := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/docker-daemon", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + Cpu: cadvisor_api.CpuStats{ + Usage: cadvisor_api.CpuUsage{ + Total: 100, + PerCpu: []uint64{5, 10}, + User: 1, + System: 1, + }, + LoadAverage: 20, + }, + }, + }, + } + metricSetKey, _ := kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "node:test/container:docker-daemon") +} + +func TestDecodeMetrics4(t *testing.T) { + kMS := kubeletMetricsSource{ + nodename: "test", + hostname: "test-hostname", + } + c1 := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "testKubelet", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + Labels: make(map[string]string), + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + Cpu: cadvisor_api.CpuStats{ + Usage: cadvisor_api.CpuUsage{ + Total: 100, + PerCpu: []uint64{5, 10}, + User: 1, + System: 1, + }, + LoadAverage: 20, + }, + }, + }, + } + + c1.Spec.Labels[kubernetesContainerLabel] = "testContainer" + c1.Spec.Labels[kubernetesPodNamespaceLabel] = "testPodNS" + c1.Spec.Labels[kubernetesPodNameLabel] = "testPodName" + metricSetKey, metricSet := kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "namespace:testPodNS/pod:testPodName/container:testContainer") + assert.Equal(t, metricSet.Labels[core.LabelMetricSetType.Key], core.MetricSetTypePodContainer) +} + +func TestDecodeMetrics5(t *testing.T) { + kMS := kubeletMetricsSource{ + nodename: "test", + hostname: "test-hostname", + } + c1 := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "k8s_test.testkubelet", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + Labels: make(map[string]string), + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + Cpu: cadvisor_api.CpuStats{ + Usage: cadvisor_api.CpuUsage{ + Total: 100, + PerCpu: []uint64{5, 10}, + User: 1, + System: 1, + }, + LoadAverage: 20, + }, + }, + }, + } + c1.Spec.Labels[kubernetesContainerLabel] = "POD" + c1.Spec.Labels[kubernetesPodNameLabel] = "testnamespace/testPodName" + metricSetKey, metricSet := kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "namespace:testnamespace/pod:testPodName") + assert.Equal(t, metricSet.Labels[core.LabelMetricSetType.Key], core.MetricSetTypePod) + + c1.Spec.Labels[kubernetesContainerLabel] = "" + c1.Spec.Labels[kubernetesPodNameLabel] = "testnamespace/testPodName" + metricSetKey, metricSet = kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "namespace:testnamespace/pod:testPodName/container:test") + assert.Equal(t, metricSet.Labels[core.LabelMetricSetType.Key], core.MetricSetTypePodContainer) +} + +func TestDecodeMetrics6(t *testing.T) { + kMS := kubeletMetricsSource{ + nodename: "test", + hostname: "test-hostname", + } + c1 := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCustomMetrics: true, + CustomMetrics: []cadvisor_api.MetricSpec{ + { + Name: "test1", + Type: cadvisor_api.MetricGauge, + Format: cadvisor_api.IntType, + }, + { + Name: "test2", + Type: cadvisor_api.MetricCumulative, + Format: cadvisor_api.IntType, + }, + { + Name: "test3", + Type: cadvisor_api.MetricGauge, + Format: cadvisor_api.FloatType, + }, + { + Name: "test4", + Type: cadvisor_api.MetricCumulative, + Format: cadvisor_api.FloatType, + }, + }, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + Cpu: cadvisor_api.CpuStats{ + Usage: cadvisor_api.CpuUsage{ + Total: 100, + PerCpu: []uint64{5, 10}, + User: 1, + System: 1, + }, + LoadAverage: 20, + }, + CustomMetrics: map[string][]cadvisor_api.MetricVal{ + "test1": []cadvisor_api.MetricVal{ + { + Label: "test1", + Timestamp: time.Now(), + IntValue: 1, + FloatValue: 1.0, + }, + }, + "test2": []cadvisor_api.MetricVal{ + { + Label: "test2", + Timestamp: time.Now(), + IntValue: 1, + FloatValue: 1.0, + }, + }, + "test3": []cadvisor_api.MetricVal{ + { + Label: "test3", + Timestamp: time.Now(), + IntValue: 1, + FloatValue: 1.0, + }, + }, + "test4": []cadvisor_api.MetricVal{ + { + Label: "test4", + Timestamp: time.Now(), + IntValue: 1, + FloatValue: 1.0, + }, + }, + }, + }, + }, + } + metricSetKey, metricSet := kMS.decodeMetrics(&c1) + assert.Equal(t, metricSetKey, "node:test") + assert.Equal(t, metricSet.Labels[core.LabelMetricSetType.Key], core.MetricSetTypeNode) +} + +var nodes = []kube_api.Node{ + kube_api.Node{ + ObjectMeta: kube_api.ObjectMeta{ + Name: "testNode", + }, + Status: kube_api.NodeStatus{ + Conditions: []kube_api.NodeCondition{ + { + Type: "NotReady", + Status: kube_api.ConditionTrue, + }, + }, + Addresses: []kube_api.NodeAddress{ + { + Type: kube_api.NodeHostName, + Address: "testNode", + }, + { + Type: kube_api.NodeInternalIP, + Address: "127.0.0.1", + }, + }, + }, + }, + kube_api.Node{ + ObjectMeta: kube_api.ObjectMeta{ + Name: "testNode", + }, + Status: kube_api.NodeStatus{ + Conditions: []kube_api.NodeCondition{ + { + Type: "NotReady", + Status: kube_api.ConditionTrue, + }, + }, + Addresses: []kube_api.NodeAddress{ + { + Type: kube_api.NodeHostName, + Address: "testNode", + }, + { + Type: kube_api.NodeLegacyHostIP, + Address: "127.0.0.1", + }, + }, + }, + }, + kube_api.Node{ + ObjectMeta: kube_api.ObjectMeta{ + Name: "testNode", + }, + Status: kube_api.NodeStatus{ + Conditions: []kube_api.NodeCondition{ + { + Type: "NotReady", + Status: kube_api.ConditionTrue, + }, + }, + Addresses: []kube_api.NodeAddress{ + { + Type: kube_api.NodeHostName, + Address: "testNode", + }, + { + Type: kube_api.NodeLegacyHostIP, + Address: "127.0.0.2", + }, + { + Type: kube_api.NodeInternalIP, + Address: "127.0.0.1", + }, + }, + }, + }, +} + +func TestGetNodeHostnameAndIP(t *testing.T) { + for _, node := range nodes { + hostname, ip, err := getNodeHostnameAndIP(&node) + assert.NoError(t, err) + assert.Equal(t, hostname, "testNode") + assert.Equal(t, ip, "127.0.0.1") + } +} + +func TestScrapeMetrics(t *testing.T) { + rootContainer := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + HasMemory: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + }, + }, + } + + subcontainer := cadvisor_api.ContainerInfo{ + ContainerReference: cadvisor_api.ContainerReference{ + Name: "/docker-daemon", + }, + Spec: cadvisor_api.ContainerSpec{ + CreationTime: time.Now(), + HasCpu: true, + HasMemory: true, + }, + Stats: []*cadvisor_api.ContainerStats{ + { + Timestamp: time.Now(), + }, + }, + } + response := map[string]cadvisor_api.ContainerInfo{ + rootContainer.Name: { + ContainerReference: cadvisor_api.ContainerReference{ + Name: rootContainer.Name, + }, + Spec: rootContainer.Spec, + Stats: []*cadvisor_api.ContainerStats{ + rootContainer.Stats[0], + }, + }, + subcontainer.Name: { + ContainerReference: cadvisor_api.ContainerReference{ + Name: subcontainer.Name, + }, + Spec: subcontainer.Spec, + Stats: []*cadvisor_api.ContainerStats{ + subcontainer.Stats[0], + }, + }, + } + data, err := json.Marshal(&response) + require.NoError(t, err) + handler := util.FakeHandler{ + StatusCode: 200, + RequestBody: "", + ResponseBody: string(data), + T: t, + } + server := httptest.NewServer(&handler) + defer server.Close() + + var client KubeletClient + + mtrcSrc := kubeletMetricsSource{ + kubeletClient: &client, + } + + split := strings.SplitN(strings.Replace(server.URL, "http://", "", 1), ":", 2) + mtrcSrc.host.IP = split[0] + mtrcSrc.host.Port, err = strconv.Atoi(split[1]) + + start := time.Now() + end := start.Add(5 * time.Second) + res := mtrcSrc.ScrapeMetrics(start, end) + assert.Equal(t, res.MetricSets["node:/container:docker-daemon"].Labels["type"], "sys_container") + assert.Equal(t, res.MetricSets["node:/container:docker-daemon"].Labels["container_name"], "docker-daemon") + +} diff --git a/vendor/k8s.io/heapster/metrics/sources/manager.go b/vendor/k8s.io/heapster/metrics/sources/manager.go new file mode 100644 index 0000000000..0333a84b22 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/manager.go @@ -0,0 +1,170 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sources + +import ( + "math/rand" + "time" + + . "k8s.io/heapster/metrics/core" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + DefaultMetricsScrapeTimeout = 20 * time.Second + MaxDelayMs = 4 * 1000 + DelayPerSourceMs = 8 +) + +var ( + // Last time Heapster performed a scrape since unix epoch in seconds. + lastScrapeTimestamp = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "heapster", + Subsystem: "scraper", + Name: "last_time_seconds", + Help: "Last time Heapster performed a scrape since unix epoch in seconds.", + }, + []string{"source"}, + ) + + // Time spent exporting scraping sources in microseconds.. + scraperDuration = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "heapster", + Subsystem: "scraper", + Name: "duration_microseconds", + Help: "Time spent scraping sources in microseconds.", + }, + []string{"source"}, + ) +) + +func init() { + prometheus.MustRegister(lastScrapeTimestamp) + prometheus.MustRegister(scraperDuration) +} + +func NewSourceManager(metricsSourceProvider MetricsSourceProvider, metricsScrapeTimeout time.Duration) (MetricsSource, error) { + return &sourceManager{ + metricsSourceProvider: metricsSourceProvider, + metricsScrapeTimeout: metricsScrapeTimeout, + }, nil +} + +type sourceManager struct { + metricsSourceProvider MetricsSourceProvider + metricsScrapeTimeout time.Duration +} + +func (this *sourceManager) Name() string { + return "source_manager" +} + +func (this *sourceManager) ScrapeMetrics(start, end time.Time) *DataBatch { + glog.V(1).Infof("Scraping metrics start: %s, end: %s", start, end) + sources := this.metricsSourceProvider.GetMetricsSources() + + responseChannel := make(chan *DataBatch) + startTime := time.Now() + timeoutTime := startTime.Add(this.metricsScrapeTimeout) + + delayMs := DelayPerSourceMs * len(sources) + if delayMs > MaxDelayMs { + delayMs = MaxDelayMs + } + + for _, source := range sources { + + go func(source MetricsSource, channel chan *DataBatch, start, end, timeoutTime time.Time, delayInMs int) { + + // Prevents network congestion. + time.Sleep(time.Duration(rand.Intn(delayMs)) * time.Millisecond) + + glog.V(2).Infof("Querying source: %s", source) + metrics := scrape(source, start, end) + now := time.Now() + if !now.Before(timeoutTime) { + glog.Warningf("Failed to get %s response in time", source) + return + } + timeForResponse := timeoutTime.Sub(now) + + select { + case channel <- metrics: + // passed the response correctly. + return + case <-time.After(timeForResponse): + glog.Warningf("Failed to send the response back %s", source) + return + } + }(source, responseChannel, start, end, timeoutTime, delayMs) + } + response := DataBatch{ + Timestamp: end, + MetricSets: map[string]*MetricSet{}, + } + + latencies := make([]int, 11) + +responseloop: + for i := range sources { + now := time.Now() + if !now.Before(timeoutTime) { + glog.Warningf("Failed to get all responses in time (got %d/%d)", i, len(sources)) + break + } + + select { + case dataBatch := <-responseChannel: + if dataBatch != nil { + for key, value := range dataBatch.MetricSets { + response.MetricSets[key] = value + } + } + latency := now.Sub(startTime) + bucket := int(latency.Seconds()) + if bucket >= len(latencies) { + bucket = len(latencies) - 1 + } + latencies[bucket]++ + + case <-time.After(timeoutTime.Sub(now)): + glog.Warningf("Failed to get all responses in time (got %d/%d)", i, len(sources)) + break responseloop + } + } + + glog.V(1).Infof("ScrapeMetrics: time: %s size: %d", time.Since(startTime), len(response.MetricSets)) + for i, value := range latencies { + glog.V(1).Infof(" scrape bucket %d: %d", i, value) + } + return &response +} + +func scrape(s MetricsSource, start, end time.Time) *DataBatch { + sourceName := s.Name() + startTime := time.Now() + defer lastScrapeTimestamp. + WithLabelValues(sourceName). + Set(float64(time.Now().Unix())) + defer scraperDuration. + WithLabelValues(sourceName). + Observe(float64(time.Since(startTime)) / float64(time.Microsecond)) + + return s.ScrapeMetrics(start, end) +} diff --git a/vendor/k8s.io/heapster/metrics/sources/manager_test.go b/vendor/k8s.io/heapster/metrics/sources/manager_test.go new file mode 100644 index 0000000000..a2f6ef8643 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/manager_test.go @@ -0,0 +1,117 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sources + +import ( + "testing" + "time" + + "k8s.io/heapster/metrics/util" +) + +func TestAllSourcesReplyInTime(t *testing.T) { + metricsSourceProvider := util.NewDummyMetricsSourceProvider( + util.NewDummyMetricsSource("s1", time.Second), + util.NewDummyMetricsSource("s2", time.Second)) + + manager, _ := NewSourceManager(metricsSourceProvider, time.Second*3) + now := time.Now() + end := now.Truncate(10 * time.Second) + dataBatch := manager.ScrapeMetrics(end.Add(-10*time.Second), end) + + elapsed := time.Now().Sub(now) + if elapsed > 3*time.Second { + t.Fatalf("ScrapeMetrics took too long: %s", elapsed) + } + + present := make(map[string]bool) + for key := range dataBatch.MetricSets { + present[key] = true + } + + if _, ok := present["s1"]; !ok { + t.Fatal("s1 not found") + } + + if _, ok := present["s2"]; !ok { + t.Fatal("s2 not found") + } +} + +func TestOneSourcesReplyInTime(t *testing.T) { + metricsSourceProvider := util.NewDummyMetricsSourceProvider( + util.NewDummyMetricsSource("s1", time.Second), + util.NewDummyMetricsSource("s2", 30*time.Second)) + + manager, _ := NewSourceManager(metricsSourceProvider, time.Second*3) + now := time.Now() + end := now.Truncate(10 * time.Second) + dataBatch := manager.ScrapeMetrics(end.Add(-10*time.Second), end) + elapsed := time.Now().Sub(now) + + if elapsed > 4*time.Second { + t.Fatalf("ScrapeMetrics took too long: %s", elapsed) + } + + if elapsed < 2*time.Second { + t.Fatalf("ScrapeMetrics took too short: %s", elapsed) + } + + present := make(map[string]bool) + for key := range dataBatch.MetricSets { + present[key] = true + } + + if _, ok := present["s1"]; !ok { + t.Fatal("s1 not found") + } + + if _, ok := present["s2"]; ok { + t.Fatal("s2 found") + } +} + +func TestNoSourcesReplyInTime(t *testing.T) { + metricsSourceProvider := util.NewDummyMetricsSourceProvider( + util.NewDummyMetricsSource("s1", 30*time.Second), + util.NewDummyMetricsSource("s2", 30*time.Second)) + + manager, _ := NewSourceManager(metricsSourceProvider, time.Second*3) + now := time.Now() + end := now.Truncate(10 * time.Second) + dataBatch := manager.ScrapeMetrics(end.Add(-10*time.Second), end) + elapsed := time.Now().Sub(now) + + if elapsed > 4*time.Second { + t.Fatalf("ScrapeMetrics took too long: %s", elapsed) + } + + if elapsed < 2*time.Second { + t.Fatalf("ScrapeMetrics took too short: %s", elapsed) + } + + present := make(map[string]bool) + for key := range dataBatch.MetricSets { + present[key] = true + } + + if _, ok := present["s1"]; ok { + t.Fatal("s1 found") + } + + if _, ok := present["s2"]; ok { + t.Fatal("s2 found") + } +} diff --git a/vendor/k8s.io/heapster/metrics/sources/summary/summary.go b/vendor/k8s.io/heapster/metrics/sources/summary/summary.go new file mode 100644 index 0000000000..18c3a599e5 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/summary/summary.go @@ -0,0 +1,462 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package summary + +import ( + "fmt" + "net/url" + "time" + + . "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/sources/kubelet" + + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus" + kube_api "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + kube_client "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats" + "k8s.io/kubernetes/pkg/version" +) + +var ( + summaryRequestLatency = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: "heapster", + Subsystem: "kubelet_summary", + Name: "request_duration_microseconds", + Help: "The Kubelet summary request latencies in microseconds.", + }, + []string{"node"}, + ) +) + +// Prefix used for the LabelResourceID for volume metrics. +const VolumeResourcePrefix = "Volume:" + +// Earliest kubelet version that serves the summary API. +var minSummaryKubeletVersion = version.MustParse("v1.2.0-alpha.8") + +func init() { + prometheus.MustRegister(summaryRequestLatency) +} + +type NodeInfo struct { + kubelet.Host + NodeName string + HostName string + HostID string + KubeletVersion string +} + +// Kubelet-provided metrics for pod and system container. +type summaryMetricsSource struct { + node NodeInfo + kubeletClient *kubelet.KubeletClient + + // Whether this node requires the fall-back source. + useFallback bool + fallback MetricsSource +} + +func NewSummaryMetricsSource(node NodeInfo, client *kubelet.KubeletClient, fallback MetricsSource) MetricsSource { + return &summaryMetricsSource{ + node: node, + kubeletClient: client, + useFallback: !summarySupported(node.KubeletVersion), + fallback: fallback, + } +} + +func (this *summaryMetricsSource) Name() string { + return this.String() +} + +func (this *summaryMetricsSource) String() string { + return fmt.Sprintf("kubelet_summary:%s:%d", this.node.IP, this.node.Port) +} + +func (this *summaryMetricsSource) ScrapeMetrics(start, end time.Time) *DataBatch { + if this.useFallback { + return this.fallback.ScrapeMetrics(start, end) + } + + result := &DataBatch{ + Timestamp: time.Now(), + MetricSets: map[string]*MetricSet{}, + } + + summary, err := func() (*stats.Summary, error) { + startTime := time.Now() + defer summaryRequestLatency.WithLabelValues(this.node.HostName).Observe(float64(time.Since(startTime))) + return this.kubeletClient.GetSummary(this.node.Host) + }() + + if err != nil { + if kubelet.IsNotFoundError(err) { + glog.Warningf("Summary not found, using fallback: %v", err) + this.useFallback = true + return this.fallback.ScrapeMetrics(start, end) + } + glog.Errorf("error while getting metrics summary from Kubelet %s(%s:%d): %v", this.node.NodeName, this.node.IP, this.node.Port, err) + return result + } + + result.MetricSets = this.decodeSummary(summary) + + return result +} + +func summarySupported(kubeletVersion string) bool { + semver, err := version.Parse(kubeletVersion) + if err != nil { + glog.Errorf("Unable to parse kubelet version: %q", kubeletVersion) + return false + } + return semver.GE(minSummaryKubeletVersion) +} + +const ( + RootFsKey = "/" + LogsKey = "logs" +) + +// For backwards compatibility, map summary system names into original names. +// TODO: Migrate to the new system names and remove this. +var systemNameMap = map[string]string{ + stats.SystemContainerRuntime: "docker-daemon", + stats.SystemContainerMisc: "system", +} + +// decodeSummary translates the kubelet stats.Summary API into the flattened heapster MetricSet API. +func (this *summaryMetricsSource) decodeSummary(summary *stats.Summary) map[string]*MetricSet { + result := map[string]*MetricSet{} + + labels := map[string]string{ + LabelNodename.Key: this.node.NodeName, + LabelHostname.Key: this.node.HostName, + LabelHostID.Key: this.node.HostID, + } + + this.decodeNodeStats(result, labels, &summary.Node) + for _, pod := range summary.Pods { + this.decodePodStats(result, labels, &pod) + } + + return result +} + +// Convenience method for labels deep copy. +func (this *summaryMetricsSource) cloneLabels(labels map[string]string) map[string]string { + clone := make(map[string]string, len(labels)) + for k, v := range labels { + clone[k] = v + } + return clone +} + +func (this *summaryMetricsSource) decodeNodeStats(metrics map[string]*MetricSet, labels map[string]string, node *stats.NodeStats) { + nodeMetrics := &MetricSet{ + Labels: this.cloneLabels(labels), + MetricValues: map[string]MetricValue{}, + LabeledMetrics: []LabeledMetric{}, + CreateTime: node.StartTime.Time, + ScrapeTime: this.getScrapeTime(node.CPU, node.Memory, node.Network), + } + nodeMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeNode + + this.decodeUptime(nodeMetrics, node.StartTime.Time) + this.decodeCPUStats(nodeMetrics, node.CPU) + this.decodeMemoryStats(nodeMetrics, node.Memory) + this.decodeNetworkStats(nodeMetrics, node.Network) + this.decodeFsStats(nodeMetrics, RootFsKey, node.Fs) + metrics[NodeKey(node.NodeName)] = nodeMetrics + + for _, container := range node.SystemContainers { + key := NodeContainerKey(node.NodeName, this.getContainerName(&container)) + containerMetrics := this.decodeContainerStats(labels, &container) + containerMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypeSystemContainer + metrics[key] = containerMetrics + } +} + +func (this *summaryMetricsSource) decodePodStats(metrics map[string]*MetricSet, nodeLabels map[string]string, pod *stats.PodStats) { + podMetrics := &MetricSet{ + Labels: this.cloneLabels(nodeLabels), + MetricValues: map[string]MetricValue{}, + LabeledMetrics: []LabeledMetric{}, + CreateTime: pod.StartTime.Time, + ScrapeTime: this.getScrapeTime(nil, nil, pod.Network), + } + ref := pod.PodRef + podMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePod + podMetrics.Labels[LabelPodId.Key] = ref.UID + podMetrics.Labels[LabelPodName.Key] = ref.Name + podMetrics.Labels[LabelNamespaceName.Key] = ref.Namespace + // Needed for backward compatibility + podMetrics.Labels[LabelPodNamespace.Key] = ref.Namespace + + this.decodeUptime(podMetrics, pod.StartTime.Time) + this.decodeNetworkStats(podMetrics, pod.Network) + for _, vol := range pod.VolumeStats { + this.decodeFsStats(podMetrics, VolumeResourcePrefix+vol.Name, &vol.FsStats) + } + metrics[PodKey(ref.Namespace, ref.Name)] = podMetrics + + for _, container := range pod.Containers { + key := PodContainerKey(ref.Namespace, ref.Name, container.Name) + metrics[key] = this.decodeContainerStats(podMetrics.Labels, &container) + } +} + +func (this *summaryMetricsSource) decodeContainerStats(podLabels map[string]string, container *stats.ContainerStats) *MetricSet { + containerMetrics := &MetricSet{ + Labels: this.cloneLabels(podLabels), + MetricValues: map[string]MetricValue{}, + LabeledMetrics: []LabeledMetric{}, + CreateTime: container.StartTime.Time, + ScrapeTime: this.getScrapeTime(container.CPU, container.Memory, nil), + } + containerMetrics.Labels[LabelMetricSetType.Key] = MetricSetTypePodContainer + containerMetrics.Labels[LabelContainerName.Key] = this.getContainerName(container) + + this.decodeUptime(containerMetrics, container.StartTime.Time) + this.decodeCPUStats(containerMetrics, container.CPU) + this.decodeMemoryStats(containerMetrics, container.Memory) + this.decodeFsStats(containerMetrics, RootFsKey, container.Rootfs) + this.decodeFsStats(containerMetrics, LogsKey, container.Logs) + this.decodeUserDefinedMetrics(containerMetrics, container.UserDefinedMetrics) + + return containerMetrics +} + +func (this *summaryMetricsSource) decodeUptime(metrics *MetricSet, startTime time.Time) { + if startTime.IsZero() { + return + } + + uptime := uint64(time.Since(startTime).Nanoseconds() / time.Millisecond.Nanoseconds()) + this.addIntMetric(metrics, &MetricUptime, &uptime) +} + +func (this *summaryMetricsSource) decodeCPUStats(metrics *MetricSet, cpu *stats.CPUStats) { + if cpu == nil { + return + } + + this.addIntMetric(metrics, &MetricCpuUsage, cpu.UsageCoreNanoSeconds) +} + +func (this *summaryMetricsSource) decodeMemoryStats(metrics *MetricSet, memory *stats.MemoryStats) { + if memory == nil { + return + } + + this.addIntMetric(metrics, &MetricMemoryUsage, memory.UsageBytes) + this.addIntMetric(metrics, &MetricMemoryWorkingSet, memory.WorkingSetBytes) + this.addIntMetric(metrics, &MetricMemoryPageFaults, memory.PageFaults) + this.addIntMetric(metrics, &MetricMemoryMajorPageFaults, memory.MajorPageFaults) +} + +func (this *summaryMetricsSource) decodeNetworkStats(metrics *MetricSet, network *stats.NetworkStats) { + if network == nil { + return + } + + this.addIntMetric(metrics, &MetricNetworkRx, network.RxBytes) + this.addIntMetric(metrics, &MetricNetworkRxErrors, network.RxErrors) + this.addIntMetric(metrics, &MetricNetworkTx, network.TxBytes) + this.addIntMetric(metrics, &MetricNetworkTxErrors, network.TxErrors) +} + +func (this *summaryMetricsSource) decodeFsStats(metrics *MetricSet, fsKey string, fs *stats.FsStats) { + if fs == nil { + return + } + + fsLabels := map[string]string{LabelResourceID.Key: fsKey} + this.addLabeledIntMetric(metrics, &MetricFilesystemUsage, fsLabels, fs.UsedBytes) + this.addLabeledIntMetric(metrics, &MetricFilesystemLimit, fsLabels, fs.CapacityBytes) + this.addLabeledIntMetric(metrics, &MetricFilesystemAvailable, fsLabels, fs.AvailableBytes) +} + +func (this *summaryMetricsSource) decodeUserDefinedMetrics(metrics *MetricSet, udm []stats.UserDefinedMetric) { + for _, metric := range udm { + mv := MetricValue{} + switch metric.Type { + case stats.MetricGauge: + mv.MetricType = MetricGauge + case stats.MetricCumulative: + mv.MetricType = MetricCumulative + case stats.MetricDelta: + mv.MetricType = MetricDelta + default: + glog.V(4).Infof("Skipping %s: unknown custom metric type: %v", metric.Name, metric.Type) + continue + } + + // TODO: Handle double-precision values. + mv.ValueType = ValueFloat + mv.FloatValue = float32(metric.Value) + + metrics.MetricValues[CustomMetricPrefix+metric.Name] = mv + } +} + +func (this *summaryMetricsSource) getScrapeTime(cpu *stats.CPUStats, memory *stats.MemoryStats, network *stats.NetworkStats) time.Time { + // Assume CPU, memory and network scrape times are the same. + switch { + case cpu != nil && !cpu.Time.IsZero(): + return cpu.Time.Time + case memory != nil && !memory.Time.IsZero(): + return memory.Time.Time + case network != nil && !network.Time.IsZero(): + return network.Time.Time + default: + return time.Time{} + } +} + +// addIntMetric is a convenience method for adding the metric and value to the metric set. +func (this *summaryMetricsSource) addIntMetric(metrics *MetricSet, metric *Metric, value *uint64) { + if value == nil { + return + } + val := MetricValue{ + ValueType: ValueInt64, + MetricType: metric.Type, + IntValue: int64(*value), + } + metrics.MetricValues[metric.Name] = val +} + +// addLabeledIntMetric is a convenience method for adding the labeled metric and value to the metric set. +func (this *summaryMetricsSource) addLabeledIntMetric(metrics *MetricSet, metric *Metric, labels map[string]string, value *uint64) { + if value == nil { + return + } + + val := LabeledMetric{ + Name: metric.Name, + Labels: labels, + MetricValue: MetricValue{ + ValueType: ValueInt64, + MetricType: metric.Type, + IntValue: int64(*value), + }, + } + metrics.LabeledMetrics = append(metrics.LabeledMetrics, val) +} + +// Translate system container names to the legacy names for backwards compatibility. +func (this *summaryMetricsSource) getContainerName(c *stats.ContainerStats) string { + if legacyName, ok := systemNameMap[c.Name]; ok { + return legacyName + } + return c.Name +} + +// TODO: The summaryProvider duplicates a lot of code from kubeletProvider, and should be refactored. +type summaryProvider struct { + nodeLister *cache.StoreToNodeLister + reflector *cache.Reflector + kubeletClient *kubelet.KubeletClient +} + +func (this *summaryProvider) GetMetricsSources() []MetricsSource { + sources := []MetricsSource{} + nodes, err := this.nodeLister.List() + if err != nil { + glog.Errorf("error while listing nodes: %v", err) + return sources + } + + for _, node := range nodes.Items { + info, err := this.getNodeInfo(&node) + if err != nil { + glog.Errorf("%v", err) + continue + } + fallback := kubelet.NewKubeletMetricsSource( + info.Host, + this.kubeletClient, + info.NodeName, + info.HostName, + info.HostID, + ) + sources = append(sources, NewSummaryMetricsSource(info, this.kubeletClient, fallback)) + } + return sources +} + +func (this *summaryProvider) getNodeInfo(node *kube_api.Node) (NodeInfo, error) { + for _, c := range node.Status.Conditions { + if c.Type == kube_api.NodeReady && c.Status != kube_api.ConditionTrue { + return NodeInfo{}, fmt.Errorf("Node %v is not ready", node.Name) + } + } + info := NodeInfo{ + NodeName: node.Name, + HostName: node.Name, + HostID: node.Spec.ExternalID, + Host: kubelet.Host{ + Port: this.kubeletClient.GetPort(), + }, + KubeletVersion: node.Status.NodeInfo.KubeletVersion, + } + + for _, addr := range node.Status.Addresses { + if addr.Type == kube_api.NodeHostName && addr.Address != "" { + info.HostName = addr.Address + } + if addr.Type == kube_api.NodeInternalIP && addr.Address != "" { + info.IP = addr.Address + } + if addr.Type == kube_api.NodeLegacyHostIP && addr.Address != "" && info.IP == "" { + info.IP = addr.Address + } + } + + if info.IP == "" { + return info, fmt.Errorf("Node %v has no valid hostname and/or IP address: %v %v", node.Name, info.HostName, info.IP) + } + + return info, nil +} + +func NewSummaryProvider(uri *url.URL) (MetricsSourceProvider, error) { + // create clients + kubeConfig, kubeletConfig, err := kubelet.GetKubeConfigs(uri) + if err != nil { + return nil, err + } + kubeClient := kube_client.NewOrDie(kubeConfig) + kubeletClient, err := kubelet.NewKubeletClient(kubeletConfig) + if err != nil { + return nil, err + } + // watch nodes + lw := cache.NewListWatchFromClient(kubeClient, "nodes", kube_api.NamespaceAll, fields.Everything()) + nodeLister := &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)} + reflector := cache.NewReflector(lw, &kube_api.Node{}, nodeLister.Store, time.Hour) + reflector.Run() + + return &summaryProvider{ + nodeLister: nodeLister, + reflector: reflector, + kubeletClient: kubeletClient, + }, nil +} diff --git a/vendor/k8s.io/heapster/metrics/sources/summary/summary_test.go b/vendor/k8s.io/heapster/metrics/sources/summary/summary_test.go new file mode 100644 index 0000000000..91904c82a9 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/sources/summary/summary_test.go @@ -0,0 +1,430 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package summary + +import ( + "encoding/json" + "net/http/httptest" + "strconv" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/heapster/metrics/core" + "k8s.io/heapster/metrics/sources/kubelet" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats" + util "k8s.io/kubernetes/pkg/util/testing" +) + +const ( + // Offsets from seed value in generated container stats. + offsetCPUUsageCores = iota + offsetCPUUsageCoreSeconds + offsetMemPageFaults + offsetMemMajorPageFaults + offsetMemUsageBytes + offsetMemWorkingSetBytes + offsetNetRxBytes + offsetNetRxErrors + offsetNetTxBytes + offsetNetTxErrors + offsetFsUsed + offsetFsCapacity + offsetFsAvailable +) + +const ( + seedNode = 0 + seedRuntime = 100 + seedKubelet = 200 + seedMisc = 300 + seedPod0 = 1000 + seedPod0Container0 = 2000 + seedPod0Container1 = 2001 + seedPod1 = 3000 + seedPod1Container = 4000 + seedPod2 = 5000 + seedPod2Container = 6000 +) + +const ( + namespace0 = "test0" + namespace1 = "test1" + + pName0 = "pod0" + pName1 = "pod1" + pName2 = "pod0" // ensure pName2 conflicts with pName0, but is in a different namespace + + cName00 = "c0" + cName01 = "c1" + cName10 = "c0" // ensure cName10 conflicts with cName02, but is in a different pod + cName20 = "c1" // ensure cName20 conflicts with cName01, but is in a different pod + namespace +) + +var ( + scrapeTime = time.Now() + startTime = time.Now().Add(-time.Minute) +) + +var nodeInfo = NodeInfo{ + NodeName: "test", + HostName: "test-hostname", + HostID: "1234567890", + KubeletVersion: "1.2", +} + +type fakeSource struct { + scraped bool +} + +func (f *fakeSource) Name() string { return "fake" } +func (f *fakeSource) ScrapeMetrics(start, end time.Time) *core.DataBatch { + f.scraped = true + return nil +} + +func testingSummaryMetricsSource() *summaryMetricsSource { + return &summaryMetricsSource{ + node: nodeInfo, + kubeletClient: &kubelet.KubeletClient{}, + useFallback: false, + fallback: &fakeSource{}, + } +} + +func TestDecodeSummaryMetrics(t *testing.T) { + ms := testingSummaryMetricsSource() + summary := stats.Summary{ + Node: stats.NodeStats{ + NodeName: nodeInfo.NodeName, + StartTime: unversioned.NewTime(startTime), + CPU: genTestSummaryCPU(seedNode), + Memory: genTestSummaryMemory(seedNode), + Network: genTestSummaryNetwork(seedNode), + SystemContainers: []stats.ContainerStats{ + genTestSummaryContainer(stats.SystemContainerKubelet, seedKubelet), + genTestSummaryContainer(stats.SystemContainerRuntime, seedRuntime), + genTestSummaryContainer(stats.SystemContainerMisc, seedMisc), + }, + Fs: genTestSummaryFsStats(seedNode), + }, + Pods: []stats.PodStats{{ + PodRef: stats.PodReference{ + Name: pName0, + Namespace: namespace0, + }, + StartTime: unversioned.NewTime(startTime), + Network: genTestSummaryNetwork(seedPod0), + Containers: []stats.ContainerStats{ + genTestSummaryContainer(cName00, seedPod0Container0), + genTestSummaryContainer(cName01, seedPod0Container1), + }, + }, { + PodRef: stats.PodReference{ + Name: pName1, + Namespace: namespace0, + }, + StartTime: unversioned.NewTime(startTime), + Network: genTestSummaryNetwork(seedPod1), + Containers: []stats.ContainerStats{ + genTestSummaryContainer(cName10, seedPod1Container), + }, + VolumeStats: []stats.VolumeStats{{ + Name: "A", + FsStats: *genTestSummaryFsStats(seedPod1), + }, { + Name: "B", + FsStats: *genTestSummaryFsStats(seedPod1), + }}, + }, { + PodRef: stats.PodReference{ + Name: pName2, + Namespace: namespace1, + }, + StartTime: unversioned.NewTime(startTime), + Network: genTestSummaryNetwork(seedPod2), + Containers: []stats.ContainerStats{ + genTestSummaryContainer(cName20, seedPod2Container), + }, + }}, + } + + containerFs := []string{"/", "logs"} + expectations := []struct { + key string + setType string + seed int + cpu bool + memory bool + network bool + fs []string + }{{ + key: core.NodeKey(nodeInfo.NodeName), + setType: core.MetricSetTypeNode, + seed: seedNode, + cpu: true, + memory: true, + network: true, + fs: []string{"/"}, + }, { + key: core.NodeContainerKey(nodeInfo.NodeName, "kubelet"), + setType: core.MetricSetTypeSystemContainer, + seed: seedKubelet, + cpu: true, + memory: true, + }, { + key: core.NodeContainerKey(nodeInfo.NodeName, "docker-daemon"), + setType: core.MetricSetTypeSystemContainer, + seed: seedRuntime, + cpu: true, + memory: true, + }, { + key: core.NodeContainerKey(nodeInfo.NodeName, "system"), + setType: core.MetricSetTypeSystemContainer, + seed: seedMisc, + cpu: true, + memory: true, + }, { + key: core.PodKey(namespace0, pName0), + setType: core.MetricSetTypePod, + seed: seedPod0, + network: true, + }, { + key: core.PodKey(namespace0, pName1), + setType: core.MetricSetTypePod, + seed: seedPod1, + network: true, + fs: []string{"Volume:A", "Volume:B"}, + }, { + key: core.PodKey(namespace1, pName2), + setType: core.MetricSetTypePod, + seed: seedPod2, + network: true, + }, { + key: core.PodContainerKey(namespace0, pName0, cName00), + setType: core.MetricSetTypePodContainer, + seed: seedPod0Container0, + cpu: true, + memory: true, + fs: containerFs, + }, { + key: core.PodContainerKey(namespace0, pName0, cName01), + setType: core.MetricSetTypePodContainer, + seed: seedPod0Container1, + cpu: true, + memory: true, + fs: containerFs, + }, { + key: core.PodContainerKey(namespace0, pName1, cName10), + setType: core.MetricSetTypePodContainer, + seed: seedPod1Container, + cpu: true, + memory: true, + fs: containerFs, + }, { + key: core.PodContainerKey(namespace1, pName2, cName20), + setType: core.MetricSetTypePodContainer, + seed: seedPod2Container, + cpu: true, + memory: true, + fs: containerFs, + }} + + metrics := ms.decodeSummary(&summary) + for _, e := range expectations { + m, ok := metrics[e.key] + if !assert.True(t, ok, "missing metric %q", e.key) { + continue + } + assert.Equal(t, m.Labels[core.LabelMetricSetType.Key], e.setType, e.key) + assert.Equal(t, m.CreateTime, startTime, e.key) + assert.Equal(t, m.ScrapeTime, scrapeTime, e.key) + if e.cpu { + checkIntMetric(t, m, e.key, core.MetricCpuUsage, e.seed+offsetCPUUsageCoreSeconds) + } + if e.memory { + checkIntMetric(t, m, e.key, core.MetricMemoryUsage, e.seed+offsetMemUsageBytes) + checkIntMetric(t, m, e.key, core.MetricMemoryWorkingSet, e.seed+offsetMemWorkingSetBytes) + checkIntMetric(t, m, e.key, core.MetricMemoryPageFaults, e.seed+offsetMemPageFaults) + checkIntMetric(t, m, e.key, core.MetricMemoryMajorPageFaults, e.seed+offsetMemMajorPageFaults) + } + if e.network { + checkIntMetric(t, m, e.key, core.MetricNetworkRx, e.seed+offsetNetRxBytes) + checkIntMetric(t, m, e.key, core.MetricNetworkRxErrors, e.seed+offsetNetRxErrors) + checkIntMetric(t, m, e.key, core.MetricNetworkTx, e.seed+offsetNetTxBytes) + checkIntMetric(t, m, e.key, core.MetricNetworkTxErrors, e.seed+offsetNetTxErrors) + } + for _, label := range e.fs { + checkFsMetric(t, m, e.key, label, core.MetricFilesystemAvailable, e.seed+offsetFsAvailable) + checkFsMetric(t, m, e.key, label, core.MetricFilesystemLimit, e.seed+offsetFsCapacity) + checkFsMetric(t, m, e.key, label, core.MetricFilesystemUsage, e.seed+offsetFsUsed) + } + delete(metrics, e.key) + } + + for k, v := range metrics { + assert.Fail(t, "unexpected metric", "%q: %+v", k, v) + } +} + +func genTestSummaryContainer(name string, seed int) stats.ContainerStats { + return stats.ContainerStats{ + Name: name, + StartTime: unversioned.NewTime(startTime), + CPU: genTestSummaryCPU(seed), + Memory: genTestSummaryMemory(seed), + Rootfs: genTestSummaryFsStats(seed), + Logs: genTestSummaryFsStats(seed), + } +} + +func genTestSummaryCPU(seed int) *stats.CPUStats { + cpu := stats.CPUStats{ + Time: unversioned.NewTime(scrapeTime), + UsageNanoCores: uint64Val(seed, offsetCPUUsageCores), + UsageCoreNanoSeconds: uint64Val(seed, offsetCPUUsageCoreSeconds), + } + *cpu.UsageNanoCores *= uint64(time.Millisecond.Nanoseconds()) + return &cpu +} + +func genTestSummaryMemory(seed int) *stats.MemoryStats { + return &stats.MemoryStats{ + Time: unversioned.NewTime(scrapeTime), + UsageBytes: uint64Val(seed, offsetMemUsageBytes), + WorkingSetBytes: uint64Val(seed, offsetMemWorkingSetBytes), + PageFaults: uint64Val(seed, offsetMemPageFaults), + MajorPageFaults: uint64Val(seed, offsetMemMajorPageFaults), + } +} + +func genTestSummaryNetwork(seed int) *stats.NetworkStats { + return &stats.NetworkStats{ + Time: unversioned.NewTime(scrapeTime), + RxBytes: uint64Val(seed, offsetNetRxBytes), + RxErrors: uint64Val(seed, offsetNetRxErrors), + TxBytes: uint64Val(seed, offsetNetTxBytes), + TxErrors: uint64Val(seed, offsetNetTxErrors), + } +} + +func genTestSummaryFsStats(seed int) *stats.FsStats { + return &stats.FsStats{ + AvailableBytes: uint64Val(seed, offsetFsAvailable), + CapacityBytes: uint64Val(seed, offsetFsCapacity), + UsedBytes: uint64Val(seed, offsetFsUsed), + } +} + +// Convenience function for taking the address of a uint64 literal. +func uint64Val(seed, offset int) *uint64 { + val := uint64(seed + offset) + return &val +} + +func checkIntMetric(t *testing.T, metrics *core.MetricSet, key string, metric core.Metric, value int) { + m, ok := metrics.MetricValues[metric.Name] + if !assert.True(t, ok, "missing %q:%q", key, metric.Name) { + return + } + assert.Equal(t, value, m.IntValue, "%q:%q", key, metric.Name) +} + +func checkFsMetric(t *testing.T, metrics *core.MetricSet, key, label string, metric core.Metric, value int) { + for _, m := range metrics.LabeledMetrics { + if m.Name == metric.Name && m.Labels[core.LabelResourceID.Key] == label { + assert.Equal(t, value, m.IntValue, "%q:%q[%s]", key, metric.Name, label) + return + } + } + assert.Fail(t, "missing filesystem metric", "%q:[%q]:%q", key, metric.Name, label) +} + +func TestScrapeSummaryMetrics(t *testing.T) { + summary := stats.Summary{ + Node: stats.NodeStats{ + NodeName: nodeInfo.NodeName, + StartTime: unversioned.NewTime(startTime), + }, + } + data, err := json.Marshal(&summary) + require.NoError(t, err) + + server := httptest.NewServer(&util.FakeHandler{ + StatusCode: 200, + ResponseBody: string(data), + T: t, + }) + defer server.Close() + + ms := testingSummaryMetricsSource() + split := strings.SplitN(strings.Replace(server.URL, "http://", "", 1), ":", 2) + ms.node.IP = split[0] + ms.node.Port, err = strconv.Atoi(split[1]) + require.NoError(t, err) + + res := ms.ScrapeMetrics(time.Now(), time.Now()) + assert.Equal(t, res.MetricSets["node:test"].Labels[core.LabelMetricSetType.Key], core.MetricSetTypeNode) +} + +func TestFallback(t *testing.T) { + server := httptest.NewServer(&util.FakeHandler{ + StatusCode: 404, + T: t, + }) + defer server.Close() + + ms := testingSummaryMetricsSource() + split := strings.SplitN(strings.Replace(server.URL, "http://", "", 1), ":", 2) + ms.node.IP = split[0] + var err error + ms.node.Port, err = strconv.Atoi(split[1]) + require.NoError(t, err) + fallback := ms.fallback.(*fakeSource) + + ms.ScrapeMetrics(time.Now(), time.Now()) + assert.True(t, fallback.scraped) + assert.True(t, ms.useFallback) + + server.Close() // Second request should not hit the server. + fallback.scraped = false // reset + ms.ScrapeMetrics(time.Now(), time.Now()) + assert.True(t, fallback.scraped) +} + +func TestSummarySupported(t *testing.T) { + tests := []struct { + version string + expectFallback bool + }{ + {"v1.2.0-alpha.8", false}, + {"v1.2.0", false}, + {"v1.2.0-alpha.6", true}, + {"v1.3.0-alpha.1", false}, + {"v1.1.8", true}, + {"v1.0.6", true}, + {"v-invalid", true}, + } + + for _, test := range tests { + node := nodeInfo + node.KubeletVersion = test.version + source := NewSummaryMetricsSource(node, nil, nil).(*summaryMetricsSource) + assert.Equal(t, test.expectFallback, source.useFallback, test.version) + } +} diff --git a/vendor/k8s.io/heapster/metrics/util/dummies.go b/vendor/k8s.io/heapster/metrics/util/dummies.go new file mode 100644 index 0000000000..856860a943 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/util/dummies.go @@ -0,0 +1,138 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "sync" + "time" + + "k8s.io/heapster/metrics/core" +) + +type DummySink struct { + name string + mutex sync.Mutex + exportCount int + stopped bool + latency time.Duration +} + +func (this *DummySink) Name() string { + return this.name +} +func (this *DummySink) ExportData(*core.DataBatch) { + this.mutex.Lock() + this.exportCount++ + this.mutex.Unlock() + + time.Sleep(this.latency) +} + +func (this *DummySink) Stop() { + this.mutex.Lock() + this.stopped = true + this.mutex.Unlock() + + time.Sleep(this.latency) +} + +func (this *DummySink) IsStopped() bool { + this.mutex.Lock() + defer this.mutex.Unlock() + return this.stopped +} + +func (this *DummySink) GetExportCount() int { + this.mutex.Lock() + defer this.mutex.Unlock() + return this.exportCount +} + +func NewDummySink(name string, latency time.Duration) *DummySink { + return &DummySink{ + name: name, + latency: latency, + exportCount: 0, + stopped: false, + } +} + +type DummyMetricsSource struct { + latency time.Duration + metricSet core.MetricSet +} + +func (this *DummyMetricsSource) Name() string { + return "dummy" +} + +func (this *DummyMetricsSource) ScrapeMetrics(start, end time.Time) *core.DataBatch { + time.Sleep(this.latency) + return &core.DataBatch{ + Timestamp: end, + MetricSets: map[string]*core.MetricSet{ + this.metricSet.Labels["name"]: &this.metricSet, + }, + } +} + +func newDummyMetricSet(name string) core.MetricSet { + return core.MetricSet{ + MetricValues: map[string]core.MetricValue{}, + Labels: map[string]string{ + "name": name, + }, + } +} + +func NewDummyMetricsSource(name string, latency time.Duration) *DummyMetricsSource { + return &DummyMetricsSource{ + latency: latency, + metricSet: newDummyMetricSet(name), + } +} + +type DummyMetricsSourceProvider struct { + sources []core.MetricsSource +} + +func (this *DummyMetricsSourceProvider) GetMetricsSources() []core.MetricsSource { + return this.sources +} + +func NewDummyMetricsSourceProvider(sources ...core.MetricsSource) *DummyMetricsSourceProvider { + return &DummyMetricsSourceProvider{ + sources: sources, + } +} + +type DummyDataProcessor struct { + latency time.Duration +} + +func (this *DummyDataProcessor) Name() string { + return "dummy" +} + +func (this *DummyDataProcessor) Process(data *core.DataBatch) (*core.DataBatch, error) { + time.Sleep(this.latency) + return data, nil +} + +func NewDummyDataProcessor(latency time.Duration) *DummyDataProcessor { + return &DummyDataProcessor{ + latency: latency, + } +} diff --git a/vendor/k8s.io/heapster/metrics/util/metrics/http.go b/vendor/k8s.io/heapster/metrics/util/metrics/http.go new file mode 100644 index 0000000000..e7df4892a3 --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/util/metrics/http.go @@ -0,0 +1,171 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "bufio" + "io" + "net" + "net/http" + "strconv" + "strings" + "time" + + "github.com/emicklei/go-restful" + "github.com/prometheus/client_golang/prometheus" +) + +// Code heavily influenced by Prometheus' `InstrumentHandlerFunc` + +var instLabels = []string{"method", "code"} + +// InstrumentRouteFunc works like Prometheus' InstrumentHandlerFunc but wraps +// the go-restful RouteFunction instead of a HandlerFunc +func InstrumentRouteFunc(handlerName string, routeFunc restful.RouteFunction) restful.RouteFunction { + opts := prometheus.SummaryOpts{ + Subsystem: "http", + ConstLabels: prometheus.Labels{"handler": handlerName}, + } + + reqCnt := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Subsystem: opts.Subsystem, + Name: "requests_total", + Help: "Total number of HTTP requests made.", + ConstLabels: opts.ConstLabels, + }, + instLabels, + ) + + opts.Name = "request_duration_microseconds" + opts.Help = "The HTTP request latencies in microseconds." + reqDur := prometheus.NewSummary(opts) + + opts.Name = "request_size_bytes" + opts.Help = "The HTTP request sizes in bytes." + reqSz := prometheus.NewSummary(opts) + + opts.Name = "response_size_bytes" + opts.Help = "The HTTP response sizes in bytes." + resSz := prometheus.NewSummary(opts) + + regReqCnt := prometheus.MustRegisterOrGet(reqCnt).(*prometheus.CounterVec) + regReqDur := prometheus.MustRegisterOrGet(reqDur).(prometheus.Summary) + regReqSz := prometheus.MustRegisterOrGet(reqSz).(prometheus.Summary) + regResSz := prometheus.MustRegisterOrGet(resSz).(prometheus.Summary) + + return restful.RouteFunction(func(request *restful.Request, response *restful.Response) { + now := time.Now() + + delegate := &responseWriterDelegator{ResponseWriter: response.ResponseWriter} + out := make(chan int) + urlLen := 0 + if request.Request.URL != nil { + urlLen = len(request.Request.URL.String()) + } + go computeApproximateRequestSize(request.Request, out, urlLen) + + _, cn := response.ResponseWriter.(http.CloseNotifier) + _, fl := response.ResponseWriter.(http.Flusher) + _, hj := response.ResponseWriter.(http.Hijacker) + _, rf := response.ResponseWriter.(io.ReaderFrom) + var rw http.ResponseWriter + if cn && fl && hj && rf { + rw = &fancyResponseWriterDelegator{delegate} + } else { + rw = delegate + } + response.ResponseWriter = rw + + routeFunc(request, response) + + elapsed := float64(time.Since(now)) / float64(time.Microsecond) + + method := strings.ToLower(request.Request.Method) + code := strconv.Itoa(delegate.status) + regReqCnt.WithLabelValues(method, code).Inc() + regReqDur.Observe(elapsed) + regResSz.Observe(float64(delegate.written)) + regReqSz.Observe(float64(<-out)) + }) +} + +func computeApproximateRequestSize(r *http.Request, out chan int, s int) { + s += len(r.Method) + s += len(r.Proto) + for name, values := range r.Header { + s += len(name) + for _, value := range values { + s += len(value) + } + } + s += len(r.Host) + + // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. + + if r.ContentLength != -1 { + s += int(r.ContentLength) + } + out <- s +} + +type responseWriterDelegator struct { + http.ResponseWriter + + handler, method string + status int + written int64 + wroteHeader bool +} + +func (r *responseWriterDelegator) WriteHeader(code int) { + r.status = code + r.wroteHeader = true + r.ResponseWriter.WriteHeader(code) +} + +func (r *responseWriterDelegator) Write(b []byte) (int, error) { + if !r.wroteHeader { + r.WriteHeader(http.StatusOK) + } + n, err := r.ResponseWriter.Write(b) + r.written += int64(n) + return n, err +} + +type fancyResponseWriterDelegator struct { + *responseWriterDelegator +} + +func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool { + return f.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +func (f *fancyResponseWriterDelegator) Flush() { + f.ResponseWriter.(http.Flusher).Flush() +} + +func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return f.ResponseWriter.(http.Hijacker).Hijack() +} + +func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) { + if !f.wroteHeader { + f.WriteHeader(http.StatusOK) + } + n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r) + f.written += n + return n, err +} diff --git a/vendor/k8s.io/heapster/metrics/util/util.go b/vendor/k8s.io/heapster/metrics/util/util.go new file mode 100644 index 0000000000..499d0e35ed --- /dev/null +++ b/vendor/k8s.io/heapster/metrics/util/util.go @@ -0,0 +1,49 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "fmt" + "sort" + "strings" + "time" +) + +// Concatenates a map of labels into a comma-separated key=value pairs. +func LabelsToString(labels map[string]string, separator string) string { + output := make([]string, 0, len(labels)) + for key, value := range labels { + output = append(output, fmt.Sprintf("%s:%s", key, value)) + } + + // Sort to produce a stable output. + sort.Strings(output) + return strings.Join(output, separator) +} + +func CopyLabels(labels map[string]string) map[string]string { + c := make(map[string]string, len(labels)) + for key, val := range labels { + c[key] = val + } + return c +} + +func GetLatest(a, b time.Time) time.Time { + if a.After(b) { + return a + } + return b +} diff --git a/vendor/k8s.io/heapster/riemann/Dockerfile b/vendor/k8s.io/heapster/riemann/Dockerfile new file mode 100644 index 0000000000..97e5106aee --- /dev/null +++ b/vendor/k8s.io/heapster/riemann/Dockerfile @@ -0,0 +1,12 @@ +FROM debian:8.1 + +ENV DEBIAN_FRONTEND noninteractive +RUN apt-get -qq update && apt-get -qq -y upgrade && apt-get -qq -y install default-jdk curl +ENV JAVA_HOME /usr/lib/jvm/default-java/jre + +## 5555 - Riemann TCP and UDP; 5556 - Riemann websocket +EXPOSE 5555 5555/udp 5556 +CMD ["/usr/bin/riemann"] + +RUN curl -so /tmp/riemann.deb https://aphyr.com/riemann/riemann_0.2.10_all.deb && dpkg -i /tmp/riemann.deb && rm -f /tmp/riemann.deb +ADD riemann.config /etc/riemann/ diff --git a/vendor/k8s.io/heapster/riemann/README.md b/vendor/k8s.io/heapster/riemann/README.md new file mode 100644 index 0000000000..6c4a791efd --- /dev/null +++ b/vendor/k8s.io/heapster/riemann/README.md @@ -0,0 +1,4 @@ +heapster_riemann +================ + +Monitoring and alerting for Kubernetes container metrics diff --git a/vendor/k8s.io/heapster/riemann/build.sh b/vendor/k8s.io/heapster/riemann/build.sh new file mode 100755 index 0000000000..2b23710629 --- /dev/null +++ b/vendor/k8s.io/heapster/riemann/build.sh @@ -0,0 +1,5 @@ +#! /bin/bash + +pushd $( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +docker build -t heapster_riemann:canary . +popd diff --git a/vendor/k8s.io/heapster/riemann/riemann-pagerduty-sample.config b/vendor/k8s.io/heapster/riemann/riemann-pagerduty-sample.config new file mode 100644 index 0000000000..979af67bfb --- /dev/null +++ b/vendor/k8s.io/heapster/riemann/riemann-pagerduty-sample.config @@ -0,0 +1,93 @@ +; -*- mode: clojure; -*- +; vim: filetype=clojure + +; Riemann example config demonstrating PagerDuty alerting on memory usage across a fleet of machines. + +(logging/init {:console true}) + +; Listen on the local interface over TCP (5555), UDP (5555), and websockets (5556) +(let [host "0.0.0.0"] + (tcp-server {:host host})) + +; Expire old events from the index every 30 seconds. +(periodically-expire 30) + +; Riemann allows us to transform multiple streams of events (e.g. reported +; memory usage and memory limit across many hosts) into a stream of result +; events (memory percentage use for each host). + +(defn with-memory-percentage [& children] + "Returns a stream which collects Heapster memory observation events per host, + and emits an event giving (usage/limit) for each observation. Events with nil + metrics are suppressed." + (where (service #"memory") + (by [:host] + (project [(service #"/usage_bytes") + (service #"/limit_bytes")] + (smap riemann.folds/quotient + (where* (fn [e] (not (nil? (:metric e)))) + (smap (fn [e] (assoc e :service "memory-usage-percent"))) + #(call-rescue % children))))))) + +; We can split streams according to threshold values, assign a state to each +; category, and then reconverge all results to pass to child streams. + +(defn instantaneous-state [warn crit & children] + "Returns a stream which marks events as: + state critical when metric >= crit + state warning when metric >= warn + state ok otherwise" + (let [state (fn [new-state] (fn [e] (assoc e :state new-state)))] + (pipe - + (splitp < metric + crit (state "critical" -) + warn (state "warning" -) + (state "ok" -)) + #(call-rescue % children)))) + +; Riemann includes adapters for many third-party services; here's an example of +; how to use PagerDuty. + +(require 'riemann.pagerduty) +(defn alert-pagerduty + "Returns a stream which watches for a given service+host to transition to + either critical or ok; on transition to critical, triggers a PagerDuty + incident, and on transition to ok, resolves any associated incidents." + [service-key] + (let [pd (pagerduty service-key)] + (changed-state {:init "ok"} + (where (state "ok") + #(info "Resolving incident in PagerDuty" %) + (:resolve pd)) + (where (state "critical") + #(info "Triggering incident in PagerDuty" %) + (:trigger pd))))) + +; Pulling it all together: +; As events come in, filter for those marked as originating from Heapster; +; plot all data in Graphite, +; calculate the memory percentage use across the fleet, +; derive a result state (e.g. "ok" or "critical") for each host, +; alert PagerDuty as needed, +; and plot the new events in Graphite as well. + +(streams + (let [pagerduty-key "pagerduty-service-uuid"] + + ; filter for Heapster events + (tagged "service=heapster" + + ; Index incoming events immediately + (index) + + ; Send all incoming events on to Graphite + (graphite {:host "graphite"}) + + ; Calculate memory percentage use, index those new events, alert if + ; the value is over threshold, and forward the result to Graphite + (with-memory-percentage + (index) + (instantaneous-state 0.75 0.9 + (alert-pagerduty pagerduty-key) + (graphite {:host "graphite"})) + )))) diff --git a/vendor/k8s.io/heapster/riemann/riemann.config b/vendor/k8s.io/heapster/riemann/riemann.config new file mode 100644 index 0000000000..bf46bb6f42 --- /dev/null +++ b/vendor/k8s.io/heapster/riemann/riemann.config @@ -0,0 +1,26 @@ +; -*- mode: clojure; -*- +; vim: filetype=clojure + +(logging/init {:console true}) + +; Listen on the local interface over TCP (5555), UDP (5555), and websockets +; (5556) +(let [host "0.0.0.0"] + (tcp-server {:host host}) + (udp-server {:host host}) + (ws-server {:host host}) + (repl-server {:host host})) + +; Expire old events from the index every 30 seconds. +(periodically-expire 30) + +(let [index (index)] + ; Inbound events will be passed to these streams: + (streams + (default :ttl 60 + ; Index all events immediately. + index + + ; For demo and testing purposes, just log received and expired events + #(info "received" %) + (expired #(info "expired" %)) ))) diff --git a/vendor/k8s.io/heapster/version/version.go b/vendor/k8s.io/heapster/version/version.go new file mode 100644 index 0000000000..b1fc19d871 --- /dev/null +++ b/vendor/k8s.io/heapster/version/version.go @@ -0,0 +1,22 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package version + +// Heapster version. Update this whenever making a new release. +// The version is of the format Major.Minor.Patch +// Increment major number for new feature additions and behavioral changes. +// Increment minor number for bug fixes and performance enhancements. +// Increment patch number for critical fixes to existing releases. +const HeapsterVersion = "1.2.0-beta.1"