中文密码与拼音输出
Mix.install([
{:exk_passwd, "~> 0.2.0"}
])
This notebook uses Chinese words as the memorable source and emits toneless ASCII Pinyin. The bundled transform is intentionally small and deterministic; validate every word in a real dictionary before use.
Load an example dictionary
chinese_words = [
"中国", # China
"世界", # world
"你好", # hello
"朋友", # friend
"爱情", # love
"家人", # family
"快乐", # happiness
"美好", # beautiful/good
"春天", # spring
"夏天", # summer
"秋天", # autumn
"冬天", # winter
"太阳", # sun
"月亮", # moon
"星星", # stars
"山水", # landscape
"花朵", # flowers
"树木", # trees
"和平", # peace
"希望" # hope
]
:ok = ExkPasswd.Dictionary.load_custom(:chinese_demo, chinese_words)
The example has only 20 choices. It demonstrates the API; it is not a production-sized password dictionary.
Verify the conversion
transform = %ExkPasswd.Transform.Pinyin{}
converted =
Map.new(chinese_words, fn word ->
{word, ExkPasswd.Transform.apply(transform, word, nil)}
end)
unmapped = Enum.filter(converted, fn {_source, output} -> output =~ ~r/[^a-z]/ end)
collisions = converted |> Map.values() |> Enum.frequencies() |> Enum.filter(fn {_, n} -> n > 1 end)
%{converted: converted, unmapped: unmapped, collisions: collisions}
Require unmapped == [] when ASCII output is a hard requirement. Review
collisions as well: toneless Pinyin is many-to-one, so different source words
can become the same password component.
Generate passwords
config =
ExkPasswd.Config.new!(
dictionary: :chinese_demo,
word_length: 2..2,
word_length_bounds: 1..10,
num_words: 3,
separator: "-",
digits: {2, 2},
padding: %{char: "", before: 0, after: 0, to_length: 0},
case_transform: :none,
meta: %{transforms: [transform]}
)
for _ <- 1..10, do: ExkPasswd.generate(config)
Examples have the shape 45-zhongguo-shijie-nihao-89. The Chinese meaning is
the memory aid; the stored password is the ASCII output.
Entropy of this example
details = ExkPasswd.Entropy.calculate_seen_detailed(config)
%{
word_entropy: Float.round(details.word_entropy, 2),
digit_entropy: Float.round(details.digit_entropy, 2),
total: Float.round(details.total, 2),
rating: ExkPasswd.Strength.rating(ExkPasswd.generate(config), config)
}
With 20 unique transformed choices, three words and four digits provide about 26.25 bits. That is intentionally shown as a weak example. Increasing only the number of selected words helps, but a substantially larger, reviewed dictionary is the better starting point.
larger_config = ExkPasswd.Config.merge!(config, num_words: 5, digits: {3, 3})
Float.round(ExkPasswd.Entropy.calculate_seen(larger_config), 2)
The 5-word/6-digit variant is about 41.54 bits for this tiny dictionary. Do not describe it as stronger than the calculation supports.
Pronunciation checks
examples = [
{"中国", "zhongguo"},
{"世界", "shijie"},
{"你好", "nihao"},
{"朋友", "pengyou"},
{"春天", "chuntian"},
{"女人", "nvren"},
{"旅行", "lvxing"},
{"学习", "xuexi"},
{"下雨", "xiayu"}
]
for {source, expected} <- examples do
actual = ExkPasswd.Transform.apply(transform, source, nil)
{source, actual, actual == expected}
end
The v spelling for ü after n and l follows common keyboard input
conventions (nv, lv). After j, q, x, and y, Pinyin orthography uses
u (ju, qu, xu, yu).
Important linguistic limitations
- Conversion is character-by-character and omits tones.
-
Context-dependent readings are not disambiguated. For example, 乐 is always
mapped to
le, so 音乐 becomesyinlerather thanyinyue. - The map currently contains 674 characters. It is not the top 500 entries of Jun Da’s frequency list: an audit found 87 of those top 500 characters absent.
- Against the audited frequency data, the mapped characters cover about 75.6% of corpus occurrences, not 95%.
- Traditional characters and rare simplified characters often pass through.
The 674 supplied mappings were checked against Unicode Unihan kMandarin
primary readings; 671 match directly, while 地 (di), 长 (chang), and 嗯
(en) use defensible alternate readings. That check supports the entries that
exist, not a broad coverage claim.
Detection helpers
alias ExkPasswd.Transform.Pinyin
%{
map_size: map_size(Pinyin.pinyin_map()),
contains_hanzi: Pinyin.contains_hanzi?("Hello世界"),
single_hanzi: Pinyin.hanzi?("中"),
latin_is_hanzi: Pinyin.hanzi?("A")
}
For a production integration, add a reviewed application-specific dictionary, assert ASCII conversion and output uniqueness during deployment, and calculate entropy from the final configured transform pipeline.